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


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.