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


1096

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

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

我怎样才能做到这一点?



3
文件已被SeattleRb镜像。
乔纳森·阿拉德2014年

1
您能否将@BlairAnderson接受的答案更改为该答案,因为现在接受的答案已经过时了。对于这个主题,这个问题在Google上显得很高!
rmcsharry16年

Answers:


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

铁路注:

如果从Rails运行任务,最好通过添加预加载环境=> [: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任务,如下所示 rake thing:work'[1,2,3]'
–terminalterminaly

36
不幸的是,zsh无法正确解析该调用,您需要在zsh上键入以下命令:rake thing:work\[1,2,3\],或这样rake 'thing:work[1,2,3]'
hutusi

1
@sakurashinken,您可以:environment从任务中删除该符号。rails应用程序有一个:environment任务...
Blair Anderson

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

然后,从命令行:

>耙my_task [1,false]
Args为:Rake :: TaskArguments类的{:arg1 =>“ 1”,:arg2 =>“ false”}
arg1是:字符串类的'1'
arg2是:类String的“ false”

>耙“ my_task [1、2]”
Args为:{:arg1 =>“ 1”,:arg2 =>“ 2”}

>耙invoke_my_task
Args为:{:arg1 =>“ 1”,:arg2 =>“ 2”}

>耙invoke_my_task_2
Args为:{:arg1 => 3,:arg2 => 4}

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

>用with_defaults耙
带有默认值的Args为:{:arg1 =>:default_1,:arg2 =>:default_2}

>用with_defaults ['x','y']耙
带有默认值的Args为:{: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 ['namespace:task']。invoke
gaqzi

1
这是一个单独的问题,Igoru,但是您的调用仅运行一次的原因是rake是面向依赖的,因此它将仅在需要时执行任务。对于一般任务,这意味着它尚未运行。要显式执行任务而不管其依赖关系或是否需要执行任务,请调用execute而不是invoke。
Nick Desjardins

10
注意:根据rake的说法,不赞成使用这种在任务中接受变量的语法:WARNING: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
Ajedi32

57
请注意,zsh无法正确解析命令行参数(zsh: no matches found: ...),因此您需要转义方括号:rake my_task\['arg1'\]。来自robots.thoughtbot.com/post/18129303042/…–
赛斯·布罗

2
@SethBro是的。如果您的评论没有被隐藏在“查看更多评论”链接的后面,那么我不会浪费10分钟来完成这项工作。
GMA 2014年

342

除了通过kch回答(抱歉,我没有找到方法发表评论):

您不必ENVrake命令之前将变量指定为变量。您可以将它们设置为通常的命令行参数,如下所示:

rake mytask var=foo

并从rake文件中将其作为ENV变量进行访问,例如:

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

2
这是IMO最好的最简单的答案。它立即起作用。到底是什么p意思?
stevec

1
@ user5783745像puts一样,而不是将value.to_s记录为标准输出,而是调用Obj.inspect并将其记录为标准输出。ruby-doc.org/core-2.0.0/Kernel.html#method-ip
kqcef

并覆盖环境变量?太棒了!
Damien Roche

耙子完全是精心设计的烂摊子,这是唯一可行的方法。不只是我,这个答案和“正确”答案的票数相同。
lzap

108

如果您想传递命名参数(例如,使用standard 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,带有任务参数的奇怪语法,为standard带来了额外的好处--option-names。我唯一的建议是使用exit而不是abortabort这样会使您在shell上返回代码1。如果rake任务是更高级别脚本的一部分,则更常见的是假设非零退出是某种类型的错误。
2013年

1
我同意乔,这是最好的答案。很自然的是,使用与将选项传递给脚本时相同的接口来传递选项以进行耙操作。
里克·史密斯-翁纳

1
我同意这是最好的答案。难道没有办法绕过丑陋--吗?像将rake参数传递给实际任务一样?喜欢task :my_task, :*args do |t, args|还是什么?
Augustin Riedinger 2014年

1
此外,我不明白{username}这里的用途。在哪里使用?为什么不在那里-u {username}?干杯
奥古斯丁·里丁格

2
Rake解析ARGV的方式已更改10.4.1并恢复10.4.2github.com/ruby/rake/commit/…–
Tombart

58

我从以下两个网站找到了答案:Net ManiacAimred

您需要版本大于0.8的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

15
此警告提示指出,此功能的格式已更改: 'task :t, arg, :needs => [deps]' is deprecated. Please use 'task :t, [args] => [deps]' instead.
madh 2012年

29

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

$ VAR=foo rake mytask

坦白地说,我希望将rake任务(这些--go --to -to -a程序)执行,我的任务可以从ARGV中获得它们。不幸的是,我不确定是否可行,但是我目前正在使用您的解决方案:rake var1 = val1 var2 = val2
JasonSmith 2010年

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

在解决之前,我不知道如何传递args和:environment:

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 2014年

2
rake.rubyforge.org/files/doc/rakefile_rdoc.html “请注意几句话。rake任务名称及其参数必须是rake的单个命令行参数。这通常意味着没有空格。如果需要空格,则应用引号将整个rake +参数字符串加引号。类似于:rake“ name [billy bob,smith]”
Gayle 2014年

19

我只是想能够运行:

$ rake some:task arg1 arg2

简单吧?(不!)

耙解释arg1arg2为任务,并试图运行它们。因此,我们只是在此之前中止。

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

    # your task...

    exit
  end
end

把它,括号!

免责声明:我希望能够在一个很小的宠物项目中做到这一点。不适合“现实世界”使用,因为您失去了链接rake任务的能力(例如rake task1 task2 task3)。海事组织不值得。只是使用丑陋rake task[arg1,arg2]


3
需要这样做,_, arg1, arg2 = ARGV因为第一个arg被视为rake任务的名称。但这exit是一个巧妙的把戏。
脂肪

rake task[arg1,arg2] && rake task2 && rake task3不知道这是否比丑陋rake task[arg1,arg2] task2 task3。虽然效率可能较低。
核弹手

_, *args = ARGV非常适合捕获所有后续参数!谢谢堆!
XtraSimplicity

12

我在rake文件中使用常规的ruby参数:

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?使用事件,以防止将null设为异常。
Andre Figueiredo

5

传递答案的方法在以上答案中是正确的。但是,要使用参数运行rake任务,在较新版本的rails中涉及的技术很少

它将与耙“ namespace: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]"and NOT 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

这是我在delay_job_active_record_threaded gem中与Rails一起使用的更扩展的示例:

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任务:

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

在传递参数时,最好的选择是输入文件,这可以是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
如果您的Rake任务需要JSON指令文件,则可能在Rake任务中做了太多事情。
ZiggyTheHamster,2015年

这是使过于简单的事情变得过于复杂的方式
jeffdill2
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.