Як передати аргументи командного рядка завданням граблі


1096

У мене є задача граблі, якій потрібно вставити значення в декілька баз даних.

Я хотів би передати це значення в завдання рейку з командного рядка або з іншого завдання грабля.

Як я можу це зробити?



3
Документи відображено SeattleRb.
— Джонатан Аллард

1
Чи можете ви змінити прийняту відповідь на відповідь від @BlairAnderson, оскільки прийнята відповідь зараз сильно застаріла. Це запитання відображається високо в Google для цієї теми!
— rmcsharry

Відповіді:


377

параметри та залежності повинні бути всередині масивів:

namespace :thing do
  desc "it does a thing"
  task :work, [:option, :foo, :bar] do |task, args|
    puts "work", args
  end

  task :another, [:option, :foo, :bar] do |task, args|
    puts "another #{args}"
    Rake::Task["thing:work"].invoke(args[:option], args[:foo], args[:bar])
    # or splat the args
    # Rake::Task["thing:work"].invoke(*args)
  end

end

Тоді

rake thing:work[1,2,3]
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

rake thing:another[1,2,3]
=> another {:option=>"1", :foo=>"2", :bar=>"3"}
=> work: {:option=>"1", :foo=>"2", :bar=>"3"}

ПРИМІТКА: змінна task- це об'єкт завдання, не дуже корисна, якщо ви не знаєте / не піклуєтесь про внутрішні Rake.

ПРИМІТКА:

Якщо виконується завдання з рейок, найкраще попередньо завантажити середовище, додавши, => [:environment]що є способом налаштування залежних завдань.

  task :work, [:option, :foo, :bar] => [:environment] do |task, args|
    puts "work", args
  end

28
Також переконайтеся, що ви не використовуєте пробіли між аргументами. Наприклад, не робіть цього: rake thing:work[1, 2, 3]оскільки він не працюватиме, ви отримаєте помилкуDon't know how to build task
— rpbaltazar

9
Також переконайтеся, що ви додаєте аргумент у рядок. наприклад, з вашого командного рядка виконайте завдання граблі таким чином rake thing:work'[1,2,3]'
— theterminalguy

36
На жаль, zsh не може правильно проаналізувати виклик, вам потрібно ввести команду на zsh так: rake thing:work\[1,2,3\]або цеrake 'thing:work[1,2,3]'
— hutusi

1
@sakurashinken ви можете видалити :environmentсимвол зі свого завдання. програми для рейок мають: завдання з навколишнього середовища ...
— Блер Андерсон

3
Замість того, щоб мати примітку, щоб пояснити це tозначає task, чому б не просто використовувати taskяк ім'я парам?
— Джошуа Пінтер

1132

Ви можете вказати формальні аргументи в рейку, додавши символьні аргументи до виклику завдання. Наприклад:

require 'rake'

task :my_task, [:arg1, :arg2] do |t, args|
  puts "Args were: #{args} of class #{args.class}"
  puts "arg1 was: '#{args[:arg1]}' of class #{args[:arg1].class}"
  puts "arg2 was: '#{args[:arg2]}' of class #{args[:arg2].class}"
end

task :invoke_my_task do
  Rake.application.invoke_task("my_task[1, 2]")
end

# or if you prefer this syntax...
task :invoke_my_task_2 do
  Rake::Task[:my_task].invoke(3, 4)
end

# a task with prerequisites passes its 
# arguments to it prerequisites
task :with_prerequisite, [:arg1, :arg2] => :my_task #<- name of prerequisite task

# to specify default values, 
# we take advantage of args being a Rake::TaskArguments object
task :with_defaults, :arg1, :arg2 do |t, args|
  args.with_defaults(:arg1 => :default_1, :arg2 => :default_2)
  puts "Args with defaults were: #{args}"
end

Потім з командного рядка:

> граблі my_task [1, помилково]
Аргументи були: {: arg1 => "1",: arg2 => "false"} класу Rake :: TaskArguments
arg1 було: '1' класу String
arg2 було: "false" класу String

> граблі "my_task [1, 2]"
Аргументи були: {: arg1 => "1",: arg2 => "2"}

> граблі invoke_my_task
Аргументи були: {: arg1 => "1",: arg2 => "2"}

> граблі invoke_my_task_2
Аргументи були: {: arg1 => 3,: arg2 => 4}

> граблі з_вимогою [5,6]
Аргументи були: {: arg1 => "5",: arg2 => "6"}

> граблі з_defaults
Аргументи з типовими значеннями були: {: arg1 =>: default_1,: arg2 =>: default_2}

> граблі з_defaults ['x', 'y']
Аргументи з типовими значеннями були: {: arg1 => "x",: arg2 => "y"}

Як показано у другому прикладі, якщо ви хочете використовувати пробіли, лапки навколо назви цілі необхідні, щоб оболонка не розщеплювала аргументи на пробіл.

Переглядаючи код у rake.rb , виявляється, що rake не розбирає рядки завдань для вилучення аргументів для передумов, тому ви не можете цього зробити task :t1 => "dep[1,2]". Єдиним способом вказати різні аргументи для необхідної умови було б явно викликати його в межах залежної дії завдання, як у :invoke_my_taskі :invoke_my_task_2.

Зауважте, що деякі снаряди (наприклад, zsh) вимагають від вас вийти з дужок: rake my_task\['arg1'\]


5
Щоб викликати завдання в просторі імен simpy, виконайте: Rake :: Task ['
— space names

1
Це окреме питання, Ігорю, але причина, коли твій дзвінок викликати лише один раз, полягає в тому, що граблі орієнтовані на залежність, тому вона виконує завдання лише в разі потреби. Для загальних завдань це означає, що вони ще не запущені. Щоб явно виконати завдання незалежно від його залежностей або якщо це потрібно, виклик виконувати замість виклику.
— Нік Дежардінс

10
Примітка: Згідно з рейком, цей синтаксис прийняття змінних у завданнях застарілий:WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
— Ajedi32

57
Зверніть увагу , що ЗШ не може коректно (розібрати аргументи командного рядка zsh: no matches found: ...), так що вам потрібно , щоб уникнути дужки: rake my_task\['arg1'\]. З сайту robots.thoughtbot.com/post/18129303042/…
— Seth Bro

2
@SethBro ТАК. Якби тільки ваш коментар не був прихований за посиланням "Дивіться більше коментарів", я б не витратив 10 хвилин на те, щоб зробити цю роботу.
— GMA

342

Крім відповіді від kch (я не знайшов, як залишити коментар до цього, вибачте):

Не потрібно вказувати змінні як ENVзмінні перед rakeкомандою. Ви можете просто встановити їх як звичайні параметри командного рядка:

rake mytask var=foo

і отримуйте доступ до таких файлів з вашого грабля, як ENV-змінних, таких як:

p ENV['var'] # => "foo"

2
Це найкраща найпростіша відповідь ІМО. Це спрацювало відразу. Що саме pозначає?
— stevec

1
@ user5783745 Like put, але замість реєстрації value.to_s для стандартного виклику, він викликає Obj.inspect і реєструє те, що стандартне. ruby-doc.org/core-2.0.0/Kernel.html#method-ip
— kqcef

І замінити змінні середовища? Фантастичний!
— Рош

Граблі вкрай переобтяжений безлад, і це єдиний спосіб, який спрацював. І це не тільки я, ця відповідь має таку ж кількість голосів, як і «правильна» відповідь.
— lzap

108

Якщо ви хочете передати названі аргументи (наприклад, зі стандартними OptionParser), ви можете використовувати щось подібне:

$ rake user:create -- --user test@example.com --pass 123

Зауважте --, це необхідно для обходу стандартних аргументів Rake. Повинно працювати з Rake 0.9.x , <= 10.3.x .

Новіші Rake змінили свій аналіз --, і тепер ви повинні переконатися, що він не переданий OptionParser#parseметоду, наприклад, зparser.parse!(ARGV[2..-1])

require 'rake'
require 'optparse'
# Rake task for creating an account

namespace :user do |args|
  desc 'Creates user account with given credentials: rake user:create'
  # environment is required to have access to Rails models
  task :create do
    options = {}
    OptionParser.new(args) do |opts|
      opts.banner = "Usage: rake user:create [options]"
      opts.on("-u", "--user {username}","User's email address", String) do |user|
        options[:user] = user
      end
      opts.on("-p", "--pass {password}","User's password", String) do |pass|
        options[:pass] = pass
      end
    end.parse!

    puts "creating user account..."
    u = Hash.new
    u[:email] = options[:user]
    u[:password] = options[:pass]
    # with some DB layer like ActiveRecord:
    # user = User.new(u); user.save!
    puts "user: " + u.to_s
    puts "account created."
    exit 0
  end
end

exit наприкінці переконайтеся, що зайві аргументи не трактуватимуться як завдання Rake.

Також ярлик для аргументів повинен працювати:

 rake user:create -- -u test@example.com -p 123

Коли сценарії граблі виглядають так, можливо, саме час шукати інший інструмент, який би дозволив це просто вийти з поля.


13
З моєї точки зору, це справді найкраща відповідь. Обхідні змінні вилучення середовища, дивний синтаксис із аргументами завдання, додаткова перевага для стандарту --option-names. Моя єдина пропозиція буде використовувати, exitа не abortяк, abortви залишите код із зворотним кодом 1 у оболонці. Якщо завдання граблі є частиною сценарію вищого рівня, частіше вважати, що ненульовий вихід - це певна помилка.
— Джо

1
Я погоджуюся з Джо, це найкраща відповідь. Природним є використання того ж інтерфейсу для передачі параметрів, щоб грабнути, як і при передачі параметрів сценарію.
— Рік Сміт-Унна

1
Я згоден, це найкраща відповідь. Хіба немає способу обійти потворне --? Начебто передавати rakeаргументи на власне завдання чи щось? Як, task :my_task, :*args do |t, args|чи щось?
— Августин Рідінгер

1
Крім того, я не розумію, для чого {username}тут. Де він використовується? Чому його там немає -u {username}? Ура
— Огастін Рідінгер

2
Спосіб, як Rake розбирає ARGV, змінювався 10.4.1і повертався 10.4.2. github.com/ruby/rake/commit/…
— Tombart

58

Я знайшов відповідь з цих двох веб-сайтів: Net Maniac та Aimred .

Для використання цієї методики потрібно мати версію> 0,8 граблі

Нормальний опис завдання граблі такий:

desc 'Task Description'
task :task_name => [:depends_on_taskA, :depends_on_taskB] do
  #interesting things
end

Для передачі аргументів виконайте три дії:

  1. Додайте назви аргументів після імені завдання, розділених комами.
  2. Поставте залежності в кінці, використовуючи: needs => [...]
  3. Місце | т, арг | після робити. (t - об’єкт для цього завдання)

Для доступу до аргументів у скрипті використовуйте args.arg_name

desc 'Takes arguments task'
task :task_name, :display_value, :display_times, :needs => [:depends_on_taskA, :depends_on_taskB] do |t, args|
  args.display_times.to_i.times do
    puts args.display_value
  end
end

Щоб викликати це завдання з командного рядка, передайте йому аргументи в [] s

rake task_name['Hello',4]

виведе

Hello
Hello
Hello
Hello

і якщо ви хочете викликати це завдання з іншого завдання та передавати йому аргументи, використовуйте виклик

task :caller do
  puts 'In Caller'
  Rake::Task[:task_name].invoke('hi',2)
end

потім команда

rake caller

виведе

In Caller
hi
hi

Я не знайшов способу передавати аргументи як частину залежності, оскільки такий код порушується:

task :caller => :task_name['hi',2]' do
   puts 'In Caller'
end

15
Формат цієї функції змінився в цьому попередження станів: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
— Мад

29

Інший часто використовуваний варіант - передавати змінні середовища. У своєму коді ви читаєте їх через ENV['VAR'], і можете передавати їх безпосередньо перед rakeкомандою, як-от

$ VAR=foo rake mytask

Відверто кажучи, я сподівався на завдання граблі - ці --go - to -програми, і моє завдання могло отримати їх від ARGV. На жаль, я не впевнений, чи це можливо, але наразі я використовую ваше рішення: rake var1 = val1 var2 = val2
— JasonSmith

3
@jhs: rake blah -- --these --go --to --a-program(зауважте, --щоб сказати рейку, що його перемикачі закінчилися), див. stackoverflow.com/questions/5086224/…
— занадто короткий

28

Насправді @Nick Desjardins відповів ідеально. Але тільки для освіти: ви можете використовувати брудний підхід: використовуючи ENVаргументи

task :my_task do
  myvar = ENV['myvar']
  puts "myvar: #{myvar}"
end 

rake my_task myvar=10
#=> myvar: 10

27

Я не міг зрозуміти, як передавати аргументи, а також: середовище, поки я не розробив це:

namespace :db do
  desc 'Export product data'
  task :export, [:file_token, :file_path] => :environment do |t, args|
    args.with_defaults(:file_token => "products", :file_path => "./lib/data/")

       #do stuff [...]

  end
end

І тоді я дзвоню так:

rake db:export['foo, /tmp/']

23
desc 'an updated version'
task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
    puts args[:arg1]
end

Для того, щоб назвати це, йди:rake task_name[hello, world]
— Dex

2
від rake.rubyforge.org/files/doc/rakefile_rdoc.html "Кілька слів обережності. Ім'я завдання Rake та його аргументи повинні бути аргументом єдиного командного рядка, щоб грабнути. Це, як правило, не має пробілів. Якщо пробіли не потрібні , тоді слід цитувати весь рядок + граблі + аргумент. Щось таке: граблі "name [billy bob, smith]" "
— Gayle

19

Я просто хотів мати можливість бігати:

$ rake some:task arg1 arg2

Просте, правда? (Ні!)

Rake інтерпретує arg1і arg2як завдання, і намагається їх виконувати. Тому ми просто перериваємо, перш ніж це станеться.

namespace :some do
  task task: :environment do
    arg1, arg2 = ARGV

    # your task...

    exit
  end
end

Візьміть це, дужки!

Відмова : Мені хотілося це зробити у досить маленькому проекті для домашніх тварин. Не призначений для використання в реальному світі, оскільки ви втрачаєте можливість ланцюгових завдань граблі (тобто rake task1 task2 task3). ІМО не варто. Просто використовуйте потворнеrake task[arg1,arg2] .


3
Потрібно зробити це _, arg1, arg2 = ARGVтак, як перший аргумент бачив назву завдання граблі. Але exitце акуратний трюк.
— жирний

rake task[arg1,arg2] && rake task2 && rake task3Не впевнений, що це менше , ніж некрасиво rake task[arg1,arg2] task2 task3. Напевно, менш ефективно.
— Ядерник

_, *args = ARGVідеально підходить для збору всіх наступних аргументів! Дякую купи!
— XtraSimplicity

12

Я використовую звичайний аргумент рубіна у файлі граблі:

DB = ARGV[1]

то я заглублюю завдання граблів у нижній частині файлу (оскільки рейка шукатиме завдання на основі цього аргументу).

task :database_name1
task :database_name2

командний рядок:

rake mytask db_name

для мене це чистіше, ніж var = foo ENV var та рішення аргументів [bla, blah2].
заглушка трохи прискіплива, але не дуже погана, якщо у вас просто кілька середовищ, які є одноразовими настройками


2
Для запобігання заморожених струнні проблем, використання dupв кінці: дБ = ARGV [1] .dup
— Джуанда

Подія краще db = ARGV[1].dup unless ARGV[1].nil?запобігти виключенню копіювання нуля.
— Andre Figueiredo

5

Способи передачі аргументу правильні у наведеній вище відповіді. Однак для запуску рейкової задачі з аргументами, в новій версії рейок є невеликий технічний рівень

Він буде працювати з граблім "простір імен: taskname ['argument1']"

Зверніть увагу на перевернуті лапки при виконанні завдання з командного рядка.


3

Для передачі аргументів до завдання за замовчуванням ви можете зробити щось подібне. Наприклад, скажіть "версія" - ваш аргумент:

task :default, [:version] => [:build]

task :build, :version do |t,args|
  version = args[:version]
  puts version ? "version is #{version}" : "no version passed"
end

Тоді ви можете назвати це так:

$ rake
no version passed

або

$ rake default[3.2.1]
version is 3.2.1

або

$ rake build[3.2.1]
version is 3.2.1

Однак я не знайшов способу уникнути вказівки імені завдання (за замовчуванням або збірки) під час передачі аргументів. Мені хотілося б почути, якщо хтось знає спосіб.


3

Мені подобається синтаксис "querystring" для передачі аргументів, особливо, коли має бути передано багато аргументів.

Приклад:

rake "mytask[width=10&height=20]"

"Рядок запитів" є:

width=10&height=20

Попередження: зауважте, що синтаксис є rake "mytask[foo=bar]"і НЕ rake mytask["foo=bar"]

Під час розбору всередині завдання з граблями Rack::Utils.parse_nested_queryми отримуємо Hash:

=> {"width"=>"10", "height"=>"20"}

(Прикольне те, що ви можете передавати хеші та масиви, докладніше нижче)

Ось як це досягти:

require 'rack/utils'

task :mytask, :args_expr do |t,args|
  args.with_defaults(:args_expr => "width=10&height=10")
  options = Rack::Utils.parse_nested_query(args[:args_expr])
end

Ось більш розширений приклад, який я використовую з Rails в моїй дорогоцінній камені delayed_job_active_record_threaded :

bundle exec rake "dj:start[ebooks[workers_number]=16&ebooks[worker_timeout]=60&albums[workers_number]=32&albums[worker_timeout]=120]"

Розбирається так само, як вище, із залежністю від середовища (для того, щоб завантажити середовище Rails)

namespace :dj do
  task :start, [ :args_expr ] => :environment do |t, args|
    # defaults here...
    options = Rack::Utils.parse_nested_query(args[:args_expr])  
  end
end

Дає наступне в options

=> {"ebooks"=>{"workers_number"=>"16", "worker_timeout"=>"60"}, "albums"=>{"workers_number"=>"32", "worker_timeout"=>"120"}}

3

Якщо ви не можете турбуватися, щоб згадати, яка позиція аргументу для чого, і ви хочете зробити щось на зразок хеш-аргументу рубіну. Ви можете використовувати один аргумент, щоб передати рядок, а потім переробити цей рядок у хеш параметрів.

namespace :dummy_data do
  desc "Tests options hash like arguments"
  task :test, [:options] => :environment do |t, args|
    arg_options = args[:options] || '' # nil catch incase no options are provided
    two_d_array = arg_options.scan(/\W*(\w*): (\w*)\W*/)
    puts two_d_array.to_s + ' # options are regexed into a 2d array'
    string_key_hash = two_d_array.to_h
    puts string_key_hash.to_s + ' # options are in a hash with keys as strings'
    options = two_d_array.map {|p| [p[0].to_sym, p[1]]}.to_h
    puts options.to_s + ' # options are in a hash with symbols'
    default_options = {users: '50', friends: '25', colour: 'red', name: 'tom'}
    options = default_options.merge(options)
    puts options.to_s + ' # default option values are merged into options'
  end
end

І в командному рядку ви отримаєте.

$ rake dummy_data:test["users: 100 friends: 50 colour: red"]
[["users", "100"], ["friends", "50"], ["colour", "red"]] # options are regexed into a 2d array
{"users"=>"100", "friends"=>"50", "colour"=>"red"} # options are in a hash with keys as strings
{:users=>"100", :friends=>"50", :colour=>"red"} # options are in a hash with symbols
{:users=>"100", :friends=>"50", :colour=>"red", :name=>"tom"} # default option values are merged into options

1
У вашому коді потрібно кілька добре розміщених порожніх рядків. Я не знаю, як ти читаєш цю стінку тексту.
— Джошуа Пінтер

2

Більшість описаних вище методів для мене не працювали, можливо, вони застаріли в новіших версіях. Оновлений посібник ви можете знайти тут: http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

скопіюйте та вставте анс із посібника тут:

task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args|
  # You can use args from here
end

Викликайте це так

bin/rake "task_name[value 1]" # entire argument string should be quoted

1

Для виконання завдань граблі в традиційному стилі аргументів:

rake task arg1 arg2

А потім скористайтеся:

task :task do |_, args|
  puts "This is argument 1: #{args.first}"
end

Додайте наступний патч з дорогоцінним каменем:

Rake::Application.class_eval do

  alias origin_top_level top_level

  def top_level
    @top_level_tasks = [top_level_tasks.join(' ')]
    origin_top_level
  end

  def parse_task_string(string) # :nodoc:
    parts = string.split ' '
    return parts.shift, parts
  end

end

Rake::Task.class_eval do

  def invoke(*args)
    invoke_with_call_chain(args, Rake::InvocationChain::EMPTY)
  end

end

-5

Під час передачі параметрів кращим варіантом є вхідний файл, чи може це бути excel json або все, що вам потрібно, і звідти читати структуру даних та потрібні змінні з тієї, що включає ім'я змінної, як це потрібно. Для читання файлу може бути така структура.

  namespace :name_sapace_task do
    desc "Description task...."
      task :name_task  => :environment do
        data =  ActiveSupport::JSON.decode(File.read(Rails.root+"public/file.json")) if defined?(data)
    # and work whit yoour data, example is data["user_id"]

    end
  end

Приклад json

{
  "name_task": "I'm a task",
  "user_id": 389,
  "users_assigned": [389,672,524],
  "task_id": 3
}

Виконання

rake :name_task 

4
Якщо вам потрібен файл інструкцій JSON для завдання Rake, ви, ймовірно, робите занадто багато речей у вашій задачі Rake.
— ZiggyTheHamster

Це спосіб надмірно ускладнювати то , що це неймовірно просто.
— jeffdill2
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.