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