【问题标题】:How to pass command line arguments to a rake task如何将命令行参数传递给 rake 任务
【发布时间】:2010-10-23 23:15:23
【问题描述】:

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

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

我该怎么做?

【问题讨论】:

标签: ruby command-line rake command-line-arguments


【解决方案1】:

您可以通过在任务调用中添加符号参数来指定 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 是:String 类的“1” arg2 是:String 类的“假” > 耙 "my_task[1, 2]" 参数为:{:arg1=>"1", :arg2=>"2"} > 耙invoke_my_task 参数为:{:arg1=>"1", :arg2=>"2"} > 耙invoke_my_task_2 参数为:{:arg1=>3, :arg2=>4} > 用_prerequisite[5,6] 耙 参数为:{:arg1=>"5", :arg2=>"6"} > 用_defaults 耙 默认参数为:{:arg1=>:default_1, :arg2=>:default_2} > 用_defaults['x','y'] 耙 默认参数为:{:arg1=>"x", :arg2=>"y"}

如第二个示例所示,如果要使用空格,则需要在目标名称周围加上引号,以防止 shell 将空格处的参数分开。

查看 rake.rb 中的代码,rake 似乎不会解析任务字符串来提取先决条件的参数,因此您不能这样做task :t1 =&gt; "dep[1,2]"。为先决条件指定不同参数的唯一方法是在相关任务操作中显式调用它,如:invoke_my_task:invoke_my_task_2

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

【讨论】:

  • 要在命名空间中调用任务 simpy 执行以下操作:Rake::Task['namespace:task'].invoke
  • 注意:根据 rake,这种在任务中接受变量的语法已被弃用:WARNING: 'task :t, arg, :needs =&gt; [deps]' is deprecated. Please use 'task :t, [args] =&gt; [deps]' instead.
  • 请注意,zsh 无法正确解析命令行参数(zsh: no matches found: ...),因此需要转义括号:rake my_task\['arg1'\]。来自robots.thoughtbot.com/post/18129303042/…
  • @SethBro 是的。如果您的评论没有隐藏在“查看更多 cmets”链接后面,我就不会浪费 10 分钟无法完成这项工作。
  • 注意:不要在参数之间添加空格。使用rake my_task[1,2] 而不是rake my_task[1, 2]。否则你会得到可怕的Don't know how to build task 'my_task[1,' 错误,你会挠头的时间比你想承认的要长。
【解决方案2】:

选项和依赖项需要在数组中:

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 运行任务,最好通过添加 =&gt; [:environment] 来预加载环境,这是一种设置依赖任务的方法。

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

【讨论】:

  • 另外,请确保不要在参数之间使用空格。例如不要这样做:rake thing:work[1, 2, 3],因为它不起作用,你会得到一个错误Don't know how to build task
  • 另外,请确保将参数括在字符串中。例如,从您的命令行运行 rake 任务,如下所示 rake thing:work'[1,2,3]'
  • 很遗憾,zsh 无法正确解析调用,你需要在 zsh 上输入这样的命令:rake thing:work\[1,2,3\],或者这个rake 'thing:work[1,2,3]'
  • @sakurashinken 您可以从您的任务中删除:environment 符号。 rails 应用程序有一个 :environment 任务...
  • 没有注释来解释t 表示task,为什么不直接使用task 作为参数名称?
【解决方案3】:

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

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

rake mytask var=foo

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

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

【讨论】:

  • 这是 IMO 最简单的答案。它立即起作用。 p 到底是什么意思?
  • @user5783745 与 puts 类似,但不是将 value.to_s 记录到标准输出,而是调用 Obj.inspect 并将其记录到标准输出。 ruby-doc.org/core-2.0.0/Kernel.html#method-i-p
  • 并覆盖环境变量?太棒了!
  • Rake 完全是过度设计的混乱,这是唯一有效的方法。而且不只是我,这个答案与“正确”答案的票数相同。
【解决方案4】:

如果您想传递命名参数(例如,使用标准 OptionParser),您可以使用以下内容:

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

注意--,这是绕过标准 Rake 参数所必需的。应该适用于 Rake 0.9.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 脚本看起来像这样时,也许是时候寻找另一个工具来实现这一点。

【讨论】:

  • 从我的角度来看,这确实是最好的答案。绕过环境变量 kludges,带有任务参数的奇怪语法,标准 --option-names 的额外好处。我唯一的建议是使用exit 而不是abort,因为abort 会给shell 留下一个返回码1。如果 rake 任务是更高级别脚本的一部分,则更常见的是假设非零退出是某种类型的错误。
  • 我同意乔的观点,这是最好的答案。很自然的事情是使用与将选项传递给脚本时相同的接口来传递选项。
  • 我同意这是最好的答案。难道没有办法绕过丑陋的--吗?就像将 rake 参数传递给实际任务之类的?比如task :my_task, :*args do |t, args| 什么的?
  • 此外,我不明白{username} 的用途。它在哪里使用?为什么-u {username} 中没有它?干杯
  • Rake 解析 ARGV 的方式在 10.4.1 中进行了更改,并在 10.4.2 中恢复了。 github.com/ruby/rake/commit/…
【解决方案5】:

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

您需要拥有 > 0.8 的 rake 版本才能使用此技术

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

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

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

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

【讨论】:

  • 此功能的格式已更改为警告状态:'task :t, arg, :needs =&gt; [deps]' is deprecated. Please use 'task :t, [args] =&gt; [deps]' instead.
【解决方案6】:

实际上@Nick Desjardins 的回答很完美。但仅用于教育:您可以使用肮脏的方法:使用ENV 参数

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

rake my_task myvar=10
#=> myvar: 10

【讨论】:

    【解决方案7】:

    在我解决这个问题之前,我无法弄清楚如何传递 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/']
    

    【讨论】:

    • 感谢您,在维护环境的同时提供了很好的解决方案
    【解决方案8】:

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

    $ VAR=foo rake mytask
    

    【讨论】:

    • 坦率地说,我希望 rake 任务 -- 这些 --go --to -a 程序和我的任务可以从 ARGV 获取它们。不幸的是,我不确定这是否可行,但我目前正在使用您的解决方案:rake var1=val1 var2=val2
    • @jhs: rake blah -- --these --go --to --a-program(注意-- 告诉rake它的开关已经结束),见stackoverflow.com/questions/5086224/…
    • @muistooshort 不幸的是(不知道它在 11 年是如何工作的)这将尝试运行所有传递的参数,就好像它们是任务一样。半丑陋的解决方案之一是基于 ARGV 创建空任务,内容因此这些任务确实会运行,它们不会做任何事情,第二个是在任务结束时exit。退出更容易,但这会破坏任何试图与其他任务一起运行退出任务的复合任务,因为exit 将停止任务执行并退出 Rake。
    • @karatedog 你确定吗?我只是试了一下以确保它看起来不错,是我遗漏了什么吗?
    • @muistooshort 对,使用双破折号传递参数有效。我无法更正之前的评论,错误在于传递 linux 样式的命令行参数,例如:--switch1 value1 --switch2 value2.
    【解决方案9】:
    desc 'an updated version'
    task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
        puts args[:arg1]
    end
    

    【讨论】:

    • 要调用它,请转到:rake task_name[hello, world]
    • from rake.rubyforge.org/files/doc/rakefile_rdoc.html "请注意几句。rake 任务名称及其参数需要是 rake 的单个命令行参数。这通常意味着没有空格。如果需要空格,则应该引用整个 rake + 参数字符串。像这样:rake "name[billy bob, smith]" "
    【解决方案10】:

    我只是希望能够跑步:

    $ rake some:task arg1 arg2
    

    很简单,对吧? (不!)

    Rake 将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]

    【讨论】:

    • 需要创建 _, arg1, arg2 = ARGV,因为第一个 arg 被视为 rake 任务的名称。但是exit 是一个巧妙的技巧。
    • rake task[arg1,arg2] &amp;&amp; rake task2 &amp;&amp; rake task3 不确定这是否比rake task[arg1,arg2] task2 task3 更丑。不过可能效率较低。
    • _, *args = ARGV 非常适合捕获所有后续参数!谢谢大家!
    【解决方案11】:

    我在 rake 文件中使用了一个常规的 ruby​​ 参数:

    DB = ARGV[1]
    

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

    task :database_name1
    task :database_name2
    

    命令行:

    rake mytask db_name
    

    我觉得这比 var=foo ENV var 和任务 args[blah, blah2] 解决方案更干净。
    存根有点笨拙,但如果您只有几个一次性设置的环境,那也不算太糟糕

    【讨论】:

    • 为防止出现冻结字符串问题,请在末尾使用dup:db = ARGV[1].dup
    • 事件更好 db = ARGV[1].dup unless ARGV[1].nil? 以防止异常重复 nil。
    • 我在尝试这个时遇到错误:rake aborted! Don't know how to build task 'hello world'
    【解决方案12】:

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

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

    注意从命令行运行任务时的倒引号。

    【讨论】:

      【解决方案13】:

      要将参数传递给默认任务,您可以执行以下操作。例如,说 “版本”是你的论点:

      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
      

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

      【讨论】:

        【解决方案14】:

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

        例子:

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

        “查询字符串”是:

        width=10&height=20
        

        警告:注意语法是rake "mytask[foo=bar]"NOT 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 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"}}
        

        【讨论】:

          【解决方案15】:

          上面描述的大多数方法对我都不起作用,也许它们在较新的版本中已被弃用。 最新指南可在此处找到: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
          

          【讨论】:

            【解决方案16】:

            如果您不想记住参数位置是什么,并且您想做一些类似 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
            

            【讨论】:

            • 您的代码需要一些放置良好的空行。我不知道你是怎么读到那堵文字墙的。
            【解决方案17】:

            使用传统的参数风格运行 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
            

            【讨论】:

              【解决方案18】:

              我在这里看不到的一件事是how to handle arbitrary arguments。如果您传递任务定义中未列出的参数,它们仍然可以在args.extras 下访问:

              task :thing, [:foo] do |task, args|
                puts args[:foo]     # named argument
                puts args.extras    # any additional arguments that were passed
              end
              

              【讨论】:

                【解决方案19】:

                在传递参数时,更好的选择是输入文件,可以是 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 
                

                【讨论】:

                • 如果您的 Rake 任务需要 JSON 指令文件,您可能在 Rake 任务中做了太多事情。
                • 这是方式过度复杂的事情,非常简单。
                • 我们使用 rake 任务来完成许多复杂的事情,例如任务。其中之一是作为 ETL 流程的输入,您可能需要许多输入字段来执行此操作。我们使用 rake 任务来执行许多复杂的事情,例如任务。其中之一是作为 ETL 流程的输入,您可能需要许多输入字段来执行此操作。如果您认为 Rake 任务仅用于最简单的事情,那么您可能没有在其他复杂的环境中使用。感谢您的评论。
                猜你喜欢
                • 2011-07-02
                • 2012-09-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-04-08
                • 1970-01-01
                相关资源
                最近更新 更多