题 如何将命令行参数传递给rake任务


我有一个rake任务,需要将值插入多个数据库。

我想将此值从命令行传递到rake任务,或者从 另一个 耙任务。

我怎样才能做到这一点?


952
2018-05-05 16:27


起源


rakefile rdoc - Brian Maltzan
文件 已被SeattleRb反映。 - Jonathan Allard
你可以把@BlairAnderson的答案改成答案,因为现在接受的答案已经过时了。对于这个话题,谷歌的这个问题很高! - rmcsharry


答案:


选项和依赖项需要在数组内部:

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内部,否则不是很有用。

铁路注意:

如果从rails运行任务,最好通过添加来预加载环境 => [:environment] 这是一种设置方式 依赖的 任务。

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

230
2018-04-07 21:56



这在2016年8月适用于m ...更新的答案应该更新到我认为! - rmcsharry
我更新了这个以接受请求作为答案。然而,它没有演示如何从任务本身将参数传递给另一个rake任务。 - Tilendor
另外,请确保不要在参数之间使用空格。例如,不要这样做: rake thing:work[1, 2, 3] 因为它不起作用,你会得到一个错误 Don't know how to build task - rpbaltazar
另外,请确保将参数括在string中。例如,从命令行运行rake任务 rake thing:work'[1,2,3]' - Damian Simon Peter
遗憾的是,zsh无法正确解析调用,您需要在zsh上键入命令,如下所示: rake thing:work\[1,2,3\], 或这个 rake 'thing:work[1,2,3]' - hutusi


您可以通过向任务调用添加符号参数来在rake中指定正式参数。例如:

require 'rake'

task :my_task, [:arg1, :arg2] do |t, args|
  puts "Args were: #{args}"
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,2]
Args是:{:arg1 =>“1”,:arg2 =>“2”}

> rake“my_task [1,2]”
Args是:{:arg1 =>“1”,:arg2 =>“2”}

> rake invoke_my_task
Args是:{:arg1 =>“1”,:arg2 =>“2”}

> rake invoke_my_task_2
Args是:{:arg1 => 3,:arg2 => 4}

> rake with_prerequisite [5,6]
Args是:{:arg1 =>“5”,:arg2 =>“6”}

> rake with_defaults
默认值为的参数为:{:arg1 =>:default_1,:arg2 =>:default_2}

> rake with_defaults ['x','y']
默认值为的参数为:{:arg1 =>“x”,:arg2 =>“y”}

如第二个示例所示,如果要使用空格,则必须使用目标名称周围的引号来防止shell拆分空间处的参数。

看看代码 rake.rb,看起来rake不会解析任务字符串来提取先决条件的参数,所以你不能这样做 task :t1 => "dep[1,2]"。为先决条件指定不同参数的唯一方法是在依赖任务操作中显式调用它,如 :invoke_my_task 和 :invoke_my_task_2

请注意,某些shell(如zsh)要求您转义括号: rake my_task\['arg1'\]


1081
2018-05-05 16:45



要在命名空间内调用任务,请执行以下操作:Rake :: Task ['namespace:task']。invoke - gaqzi
这是一个单独的问题,Igoru,但你调用只调用一次的原因是rake是面向依赖的,所以它只会在需要时执行一个任务。对于通用任务,这意味着它尚未运行。要显式执行任务而不管其依赖性或是否需要,请调用execute而不是invoke。 - Nick Desjardins
注意:根据rake,不推荐使用这种在任务中接受变量的语法: WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead. - Ajedi32
请注意,zsh无法正确解析命令行参数(zsh: no matches found: ...),所以你需要逃避括号: rake my_task\['arg1'\]。从 robots.thoughtbot.com/post/18129303042/... - Seth Bro
@SethBro是的。如果只有你的评论没有隐藏在“查看更多评论”链接后面,我就不会浪费10分钟无法完成这项工作。 - GMA


除了回答kch(我没有找到如何发表评论,抱歉):

您不必将变量指定为 ENV 之前的变量 rake 命令。您可以像往常一样将它们设置为通常的命令行参数:

rake mytask var=foo

并从您的rake文件中访问那些作为ENV变量,如下所示:

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

287
2018-02-19 11:05



这真的很方便。谢谢! - Hoang Le
啊,好吧 这个 是Rails如何做到的。说得通... - Ajedi32


如果要传递命名参数(例如使用标准参数) 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脚本看起来像这样时,也许是时候寻找另一个允许这个开箱即用的工具了。


94
2017-10-19 13:09



从我的角度来看,这确实是最好的答案。绕过环境变量kludges,使用任务参数的奇怪语法,标准的额外好处 --option-names。我唯一的建议是使用 exit 而不是 abort 如 abort 将给你留下一个返回码为1的shell。如果rake任务是更高级别脚本的一部分,则更常见的是假设非零退出是某种类型的错误。 - Joe
我同意乔,这是最好的答案。自然是使用相同的接口将选项传递给rake,就像将选项传递给脚本一样。 - Rik Smith-Unna
我同意这是最好的答案。难道没有办法绕过丑陋 --?喜欢路过 rake 对实际任务的争论还是什么?喜欢 task :my_task, :*args do |t, args| 或者其他的东西? - Augustin Riedinger
另外,我不明白是什么 {username} 在这里。在哪里使用?为什么不在那里 -u {username}?干杯 - Augustin Riedinger
Rake解析ARGV的方式发生了变化 10.4.1 并且还原了 10.4.2。 github.com/ruby/rake/commit/... - Tombart


我从这两个网站找到了答案: 净疯子 和 Aimred

你需要有版本> 0.8的rake来使用这种技术

正常的rake任务描述如下:

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

要传递参数,请执行以下三项操作:

  1. 在任务名称后添加参数名称,以逗号分隔。
  2. 使用:needs => [...]将依赖项放在最后
  3. 放置| t,args |做完之后。 (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

如果要从另一个任务调用此任务,并传递参数,请使用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

56
2018-05-05 16:51



此警告的格式已更改,因为此警告指出: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead. - madh


另一个常用选项是传递环境变量。在你的代码中,你通过它阅读它们 ENV['VAR'],并且可以在它之前传递它们 rake 命令,就像

$ VAR=foo rake mytask

30
2018-05-05 16:53



坦率地说,我希望得到rake任务 - 这些--go - to -a程序,我的任务可以从ARGV获得。不幸的是,我不确定这是否可能,但我目前正在使用您的解决方案:rake var1 = val1 var2 = val2 - JasonSmith
@jhs: rake blah -- --these --go --to --a-program (注意 -- 告诉耙它的开关已经结束了,见 stackoverflow.com/questions/5086224/... - mu is too short


实际上@Nick Desjardins回答得很完美。但仅仅是为了教育:你可以使用肮脏的方法:使用 ENV 论据

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

rake my_task myvar=10
#=> myvar: 10

30
2018-02-28 20:06





在我解决这个问题之前,我无法弄清楚如何传递args以及:环境:

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

24
2017-11-29 19:38