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

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

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

我该怎么做?

410903 次浏览

您可以通过向任务调用添加符号参数来指定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 doRake.application.invoke_task("my_task[1, 2]")end
# or if you prefer this syntax...task :invoke_my_task_2 doRake::Task[:my_task].invoke(3, 4)end
# a task with prerequisites passes its# arguments to it prerequisitestask :with_prerequisite, [:arg1, :arg2] => :my_task #<- name of prerequisite task
# to specify default values,# we take advantage of args being a Rake::TaskArguments objecttask :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]Args were: {:arg1=>"1", :arg2=>"false"} of class Rake::TaskArgumentsarg1 was: '1' of class Stringarg2 was: 'false' of class String
> rake "my_task[1, 2]"Args were: {:arg1=>"1", :arg2=>"2"}
> rake invoke_my_taskArgs were: {:arg1=>"1", :arg2=>"2"}
> rake invoke_my_task_2Args were: {:arg1=>3, :arg2=>4}
> rake with_prerequisite[5,6]Args were: {:arg1=>"5", :arg2=>"6"}
> rake with_defaultsArgs with defaults were: {:arg1=>:default_1, :arg2=>:default_2}
> rake with_defaults['x','y']Args with defaults were: {: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'\]

我从这两个网站找到了答案:网络狂人目标

你需要有版本>0.8的耙使用此技术

正常的rake任务描述是这样的:

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

要传递参数,请做三件事:

  1. 在任务名称之后添加参数名称,用逗号分隔。
  2. 将依赖项放在末尾:需要=>[…]
  3. 在do之后放置|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 doputs args.display_valueendend

要从命令行调用此任务,请将[]s中的参数传递给它

rake task_name['Hello',4]

将输出

HelloHelloHelloHello

如果您想从另一个任务调用此任务并传递参数,请使用调用

task :caller doputs 'In Caller'Rake::Task[:task_name].invoke('hi',2)end

然后命令

rake caller

将输出

In Callerhihi

我没有找到将参数作为依赖项的一部分传递的方法,因为以下代码中断了:

task :caller => :task_name['hi',2]' doputs 'In Caller'end

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

$ VAR=foo rake mytask

除了由kch回答(我没有找到如何留下评论,对不起):

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

rake mytask var=foo

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

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

实际上@Nick Desjardins回答得很好。但只是为了教育:你可以使用肮脏的方法:使用ENV参数

task :my_task domyvar = ENV['myvar']puts "myvar: #{myvar}"end
rake my_task myvar=10#=> myvar: 10
desc 'an updated version'task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|puts args[:arg1]end

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

DB = ARGV[1]

然后我存根了文件底部的rake任务(因为rake将根据该参数名称查找任务)。

task :database_name1task :database_name2

命令行:

rake mytask db_name

我觉得这比var=foo ENV var和任务args[blah, blah2]解决方案更清晰。
存根是有点jenky,但不是太糟糕,如果你只是有几个环境,是一个一次性的设置

如果你想传递命名参数(例如标准OptionParser),你可以使用这样的东西:

$ rake user:create -- --user test@example.com --pass 123

注意--,这是绕过标准Rake参数所必需的。应该适用于Rake0.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 modelstask :create dooptions = {}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] = userendopts.on("-p", "--pass {password}","User's password", String) do |pass|options[:pass] = passendend.parse!
puts "creating user account..."u = Hash.newu[:email] = options[:user]u[:password] = options[:pass]# with some DB layer like ActiveRecord:# user = User.new(u); user.save!puts "user: " + u.to_sputs "account created."exit 0endend

最后的exit将确保额外的参数不会被解释为Rake任务。

参数的快捷方式也应该有效:

 rake user:create -- -u test@example.com -p 123

当rake脚本看起来像这样时,也许是时候寻找另一个允许开箱即用的工具了。

要将参数传递给默认任务,您可以这样做。例如,说“版本”是您的参数:

task :default, [:version] => [:build]
task :build, :version do |t,args|version = args[:version]puts version ? "version is #{version}" : "no version passed"end

你可以这样称呼它:

$ rakeno version passed

$ rake default[3.2.1]version is 3.2.1

$ rake build[3.2.1]version is 3.2.1

但是,我还没有找到一种方法来避免在传入参数时指定任务名称(默认或构建)。很想听听是否有人知道一种方法。

我喜欢参数传递的“查询字符串”语法,特别是当有很多参数要传递时。

示例:

rake "mytask[width=10&height=20]"

“查询字符串”是:

width=10&height=20

警告:注意语法是rake "mytask[foo=bar]"不是rake mytask["foo=bar"]

当使用Rack::Utils.parse_nested_query在rake任务中解析时,我们得到一个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

这是我在delayed_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 dotask :start, [ :args_expr ] => :environment do |t, args|# defaults here...options = Rack::Utils.parse_nested_query(args[:args_expr])endend

options中给出以下内容

=> {"ebooks"=>{"workers_number"=>"16", "worker_timeout"=>"60"}, "albums"=>{"workers_number"=>"32", "worker_timeout"=>"120"}}

在传递参数时,更好的选择是输入文件,可以是excel、json或任何你需要的东西,从那里读取你需要的数据结构和变量,包括需要的变量名。读取文件可以有以下结构。

  namespace :name_sapace_task dodesc "Description task...."task :name_task  => :environment dodata =  ActiveSupport::JSON.decode(File.read(Rails.root+"public/file.json")) if defined?(data)# and work whit yoour data, example is data["user_id"]
endend

示例json

{"name_task": "I'm a task","user_id": 389,"users_assigned": [389,672,524],"task_id": 3}

执行

rake :name_task

传递参数的方法在上面的答案中是正确的。然而,要使用参数运行rake任务,较新版本的rails中涉及一个小技术问题

它将与rake一起使用"namespace: taskname['artument1']"

请注意从命令行运行任务时的反转引号。

我不知道如何传递args和:环境,直到我解决了这个问题:

namespace :db dodesc '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 [...]
endend

然后我这样叫:

rake db:export['foo, /tmp/']

上面描述的大多数方法对我都不起作用,也许它们在较新版本中已被弃用。最新的指南可以在这里找到:http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

从指南中复制和粘贴ans在这里:

task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args|# You can use args from hereend

像这样调用它

bin/rake "task_name[value 1]" # entire argument string should be quoted

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

namespace :thing dodesc "it does a thing"task :work, [:option, :foo, :bar] do |task, args|puts "work", argsend  
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", argsend

我只想能够跑:

$ rake some:task arg1 arg2

很简单,对吧?(不!)

Rake将arg1arg2解释为任务,并尝试运行它们。所以我们只是在它之前中止。

namespace :some dotask task: :environment doarg1, arg2 = ARGV
# your task...
exitendend

拿着,括号!

免责声明:我希望能够在一个非常小的宠物项目中做到这一点。不适合“现实世界”使用,因为你失去了链接耙任务的能力(即rake task1 task2 task3)。在我看来不值得。只要使用丑陋的rake task[arg1,arg2]

如果您不想费心记住什么参数位置是什么,并且您想做一些类似ruby参数哈希的事情。您可以使用一个参数传入字符串,然后将该字符串正则表达式转换为选项哈希。

namespace :dummy_data dodesc "Tests options hash like arguments"task :test, [:options] => :environment do |t, args|arg_options = args[:options] || '' # nil catch incase no options are providedtwo_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_hputs 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_hputs 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'endend

在你得到的命令行上。

$ 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

要使用传统参数样式运行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_levelend
def parse_task_string(string) # :nodoc:parts = string.split ' 'return parts.shift, partsend
end
Rake::Task.class_eval do
def invoke(*args)invoke_with_call_chain(args, Rake::InvocationChain::EMPTY)end
end

我在这里看不到的一件事是如何处理任意参数。如果你传递的参数没有在任务定义中列出,它们仍然可以在args.extras下访问:

task :thing, [:foo] do |task, args|puts args[:foo]     # named argumentputs args.extras    # any additional arguments that were passedend
namespace :namespace1 dotask :task1, [:arg1, :arg2, :arg3] => :environment do |_t, args|p args[:arg1]endend

呼吁

rake namespace1:task1["1","2","3"]

调用时无需提供环境

在zsh中需要用引号括住调用

rake 'namespace1:task1["1","2","3"]'