Как передать аргументы командной строки в задачу rake


1096

У меня есть грабли задача, которая должна вставить значение в несколько баз данных.

Я хотел бы передать это значение в задачу rake из командной строки или из другой задачи rake.

Как я могу это сделать?



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\]или thisrake 'thing:work[1,2,3]'
hutusi

1
@sakurashinken вы можете удалить :environmentсимвол из вашей задачи. У приложений rails есть: задача среды ...
Блэр Андерсон

3
Вместо того , чтобы иметь записку , чтобы объяснить , что tсредства task, почему бы не просто использовать в taskкачестве имени пар?
Джошуа Пинтер

1132

Вы можете указать формальные аргументы в rake, добавив символьные аргументы к вызову задачи. Например:

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

Затем из командной строки:

> rake my_task [1, false]
Аргументы были: {: 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"}

> грабли с_дефолтами
Аргументы со значениями по умолчанию: {: arg1 =>: default_1,: arg2 =>: default_2}

> грабли with_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
Чтобы вызвать задачу из пространства имен, просто выполните: Rake :: Task ['namespace: task'].
Invoke

1
Игорь, это отдельный вопрос, но причина, по которой ваш вызов вызывается только один раз, заключается в том, что rake ориентирован на зависимость, поэтому он будет выполнять задачу только в случае необходимости. Для общих задач это означает, что он еще не запущен. Чтобы явно выполнить задачу независимо от ее зависимостей или, если это необходимо, вызовите execute вместо invoke.
Ник Дежарден

10
Примечание: согласно rake, этот синтаксис для принятия переменных в задачах устарел: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/…
Сет Бро

2
@ СэтБро ДА. Если бы только ваш комментарий не был скрыт за ссылкой «Просмотреть больше комментариев», я бы не потратил 10 минут на то, чтобы сделать эту работу.
GMA

342

В дополнение к ответу по kch (я не нашел, как оставить комментарий к этому, извините):

Вам не нужно указывать переменные как ENVпеременные перед rakeкомандой. Вы можете просто установить их как обычные параметры командной строки:

rake mytask var=foo

и получить доступ к ним из вашего файла rake как переменные ENV, например:

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

2
Это лучший простой ответ ИМО. Это сработало сразу. Что именно pозначает?
Stevec

1
@ user5783745 Подобно 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

Когда rake-скрипты выглядят так, возможно, пришло время искать другой инструмент, который позволил бы это просто из коробки.


13
С моей точки зрения это действительно лучший ответ. Обход переменных окружения kludges, странный синтаксис с аргументами задачи, дополнительное преимущество для стандарта --option-names. Мое единственное предложение было бы использовать , exitа не , abortкак abortоставит вас с кодом возврата 1 к оболочке. Если задача rake является частью скрипта более высокого уровня, более распространенным является предположение, что ненулевой выход - это ошибка определенного типа.
Джо

1
Я согласен с Джо, это лучший ответ. Естественно, для передачи параметров в rake используется тот же интерфейс, что и при передаче параметров в сценарий.
Рик Смит-Унна

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/…
Томбарт

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. Place | t, args | после того как (т является объектом для этой задачи)

Чтобы получить доступ к аргументам в сценарии, используйте 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

и если вы хотите вызвать эту задачу из другой задачи и передать ей аргументы, используйте invoke

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.
Madh

29

Другой часто используемый параметр - передавать переменные окружения. В вашем коде вы читаете их через ENV['VAR'], и можете передать их прямо перед rakeкомандой, как

$ VAR=foo rake mytask

Честно говоря, я надеялся на грабли - эти программы --go --to -a, и моя задача могла получить их от ARGV. К сожалению, я не уверен, возможно ли это, однако в настоящее время я использую ваше решение: rake var1 = val1 var2 = val2
JasonSmith

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

28

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

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

rake my_task myvar=10
#=> myvar: 10

28

Я не мог понять, как передавать аргументы, а также окружение, пока не решил это:

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/']

Спасибо за это, отличное решение при поддержке: среды
Оливье JM

24
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
from rake.rubyforge.org/files/doc/rakefile_rdoc.html "Несколько слов предостережения. Имя задачи rake и ее аргументы должны быть одним аргументом командной строки для rake. Обычно это означает отсутствие пробелов. Если нужны пробелы , тогда вся строка rake + аргумент должна быть заключена в кавычки. Примерно так: rake "name [billy bob, smith]" "
Gayle

19

Я просто хотел иметь возможность бежать:

$ rake some:task arg1 arg2

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

Рейк интерпретирует 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

Я использую обычный аргумент ruby ​​в файле rake:

DB = ARGV[1]

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

task :database_name1
task :database_name2

командная строка:

rake mytask db_name

это кажется мне чище, чем var = foo ENV var и args [blah, blah2].
заглушка немного дрянная, но не слишком плохая, если у вас есть несколько сред, которые являются одноразовыми


2
Чтобы предотвратить проблемы с замороженными строками, используйте dupв конце: db = ARGV [1] .dup
Juanda

Событие лучше, db = ARGV[1].dup unless ARGV[1].nil?чтобы исключить исключение дублирования нуля.
Андре Фигейредо

5

Способы передачи аргумента верны в ответе выше. Однако, чтобы выполнить задачу rake с аргументами, в более новой версии rails есть небольшая техническая составляющая.

Он будет работать с граблями "namespace: taskname ['arguments1']"

Обратите внимание на кавычки при запуске задачи из командной строки.


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"]

При анализе внутри задачи rake 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

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

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 Gem:

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

При передаче параметров лучше выбрать входной файл, это может быть файл 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.