关于ruby:如何将命令行参数传递给rake任务

How to pass command line arguments to a rake task

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

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

我该怎么做?


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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

然后,从命令行:

1
2
3
> rake my_task[1,2]
Args were: {:arg1=>"1", :arg2=>"2
<div class="
suo-content">[collapse title=""]<ul><li>这并不能告诉我如何用另一个任务的参数运行rake任务。它只涉及命令行的使用</li><li>要在命名空间内调用任务,请执行以下操作:rake::task['namespace:task']。</li><li>实际上我很惊讶,我已经找了很多次这个问题的答案,它一直是rake任务arg1=2 arg2=3。当参数是串联的时,这就简单多了。</li><li>谢谢,我特别需要将参数传递给先决条件任务,您的示例工作得很好。</li><li>@罗伯,@nick:"特别需要把论点传递给先决条件任务。"我看不到一个向prereq任务显式传递参数的示例。我错过什么了吗?有没有办法做到这一点,而不是调用?</li><li>是否可以连续多次调用任务?我试过<wyn>5.times { Rake::Task[:my_task].invoke }</wyn>,但它只第一次起作用。</li><li>这是一个单独的问题,igoru,但是调用只运行一次的原因是rake是面向依赖的,所以它只在需要时执行任务。对于一般任务,这意味着如果它还没有运行。若要显式执行任务,而不考虑其依赖项,或者如果需要,请调用Execute而不是Invoke。</li><li>注意:根据rake的说法,不赞成在任务中接受变量的语法:<wyn>WARNING: 'task :t, arg, :needs => [deps]' is deprecated.  Please use 'task :t, [args] => [deps]' instead.</wyn>。</li><li>请注意,zsh无法正确解析命令行参数(<wyn>zsh: no matches found: ...</wyn>),因此需要转义括号:<wyn>rake my_task\['arg1'\]</wyn>。来自robots.thoughtbot.com/post/18129303042/&hellip;</li><li>是的。如果你的评论没有被隐藏在"查看更多评论"链接后面,我就不会浪费10分钟来完成这项工作。</li><li>在你的<wyn>.zshrc</wyn>中加上<wyn>alias rake='noglob rake'</wyn>,然后忘记从括号中逃出。</li><li>当前轨道(5)的有效语法是:<wyn>task :task_name, [:var1, :var2] => :environment do |t, vars|</wyn>。内部任务变量看起来像:<wyn>{:var1 => val, :var2 => val}</wyn></li><li>为了完整性:任务不能显式地为其依赖任务的参数指定(或重写)值。</li><li>注意:不要在参数之间添加空格。用<wyn>rake my_task[1,2]</wyn>代替<wyn>rake my_task[1, 2]</wyn>。否则你会犯下可怕的错误,你会比你想承认的时间更长。</li></ul>[/collapse]</div><hr><P>除了由KCH回复(我没有找到如何对此发表评论,抱歉):</P><P>在<wyn>rake</wyn>命令之前,不必将变量指定为<wyn>ENV</wyn>变量。您可以像通常那样设置命令行参数:</P>[cc lang="ruby"]rake mytask var=foo

从rake文件中访问这些变量作为env变量,比如:

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

然后

1
2
3
rake thing:work[1,2,3]
=> work: {:option=>"1", :foo=>"2", :bar=>"3
<div class="
suo-content">[collapse title=""]<ul><li>另外,确保参数之间不使用空格。例如,不要这样做:<wyn>rake thing:work[1, 2, 3]</wyn>因为它不起作用,你会得到一个错误<wyn>Don't know how to build task</wyn>。</li><li>另外,请确保将参数括在字符串中。例如,从命令行运行rake任务,如<wyn>rake thing:work'[1,2,3]'</wyn></li><li>@DamianSimonPeter不需要使用字符串。可以简单地执行EDOCX1[24]值将是字符串!</li><li>不幸的是,zsh无法正确解析调用,您需要在zsh上键入如下命令:<wyn>rake thing:work\[1,2,3\]</wyn>,或者这个<wyn>rake 'thing:work[1,2,3]'</wyn>。</li><li>这对我来说是失败的,错误是:<wyn>Don't know how to build task 'environment' (see --tasks)</wyn>nick desjardins的回答非常有效。</li><li>@樱城建你可以从你的任务中删除<wyn>:environment</wyn>符号。Rails应用程序有一个:环境任务…</li><li>为什么不直接用<wyn>task</wyn>作为参数名,而不用说明<wyn>t</wyn>表示<wyn>task</wyn>?</li><li>@Blairanderson直言不讳。我喜欢它!-)</li><li>rake任务的名称、依赖项和参数的布局几乎是胡说八道。这个结论——虽然有效——并不是你凭直觉就能得出的。</li></ul>[/collapse]</div><hr><P>如果要传递命名参数(例如,使用标准<wyn>OptionParser</wyn>时),可以使用如下方法:</P>[cc lang="ruby"]$ rake user:create -- --user [email protected] --pass 123

注意--,这是绕过标准rake参数所必需的。应使用Rake 0.9.x,<=10.3.x。

较新的rake已经改变了对--的解析,现在您必须确保它没有传递给OptionParser#parse方法,例如parser.parse!(ARGV[2..-1])方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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任务。

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

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

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


I've found the answer from these two websites: Net Maniac and Aimred.

You need to have version > 0.8 of rake to use this technique

The normal rake task description is this:

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

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

  • 在任务名后添加参数名,用逗号分隔。
  • 使用:needs=>[…]
  • 将t,args放在Do之后。(t是此任务的对象)
  • 要访问脚本中的参数,请使用args.arg_name

    1
    2
    3
    4
    5
    6
    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

    若要从命令行调用此任务,请将参数传递给它。

    1
    rake task_name['Hello',4]

    意志输出

    1
    2
    3
    4
    Hello
    Hello
    Hello
    Hello

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

    1
    2
    3
    4
    task :caller do
      puts 'In Caller'
      Rake::Task[:task_name].invoke('hi',2)
    end

    然后命令

    1
    rake caller

    意志输出

    1
    2
    3
    In Caller
    hi
    hi

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

    1
    2
    3
    task :caller => :task_name['hi',2]' do
       puts '
    In Caller'
    end


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

    1
    $ VAR=foo rake mytask


    实际上,@nick desjardins的回答很完美。但仅仅是为了教育:你可以使用肮脏的方法:使用ENV论点

    1
    2
    3
    4
    5
    6
    7
    task :my_task do
      myvar = ENV['myvar']
      puts"myvar: #{myvar}"
    end

    rake my_task myvar=10
    #=> myvar: 10

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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

    然后我这样叫:

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

    1
    2
    3
    4
    desc 'an updated version'
    task :task_name, [:arg1, :arg2] => [:dependency1, :dependency2] do |t, args|
        puts args[:arg1]
    end


    I just wanted to be able to run:

    1
    $ rake some:task arg1 arg2

    简单,对吧?(不!)

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    namespace :some do
      task task: :environment do
        arg1, arg2 = ARGV

        # your task...

        exit
      end
    end

    拿着,支架!

    免责声明:我想在一个非常小的宠物项目中做到这一点。不适用于"真实世界",因为您失去了链rake任务的能力(即rake task1 task2 task3)。我不值得。只需使用丑陋的rake task[arg1,arg2]


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

    1
    DB = ARGV[1]

    然后我在文件的底部截取rake任务(因为rake将根据这个参数名查找任务)。

    1
    2
    task :database_name1
    task :database_name2

    命令行:

    1
    rake mytask db_name

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


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

    它将与rake"namespace:taskname['argument1']

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


    我喜欢参数传递的"querystring"语法,特别是当有很多参数要传递时。

    例子:

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

    "querystring"是:

    1
    width=10&height=20

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

    当使用Rack::Utils.parse_nested_query在rake任务中解析时,我们得到一个Hash

    1
    2
    3
    4
    5
    6
    7
    => {"width"=>"10","height"=>"20
    <div class="
    suo-content">[collapse title=""]<ul><li>投反对票的人,想详细说明吗?</li></ul>[/collapse]</div><hr><P>要将参数传递给默认任务,可以这样做。例如,假设"版本"是您的论点:</P>[cc lang="ruby"]task :default, [:version] => [:build]

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

    然后你可以这样称呼它:

    1
    2
    $ rake
    no version passed

    1
    2
    $ rake default[3.2.1]
    version is 3.2.1

    1
    2
    $ rake build[3.2.1]
    version is 3.2.1

    但是,在传递参数时,我找不到避免指定任务名称(默认或生成)的方法。如果有人知道一种方法,我会很高兴听到的。


    上面描述的大多数方法对我来说都不起作用,也许它们在新版本中被否决了。最新的指南可以在这里找到:http://guides.rubyonrails.org/command_line.html自定义rake任务

    从指南中复制并粘贴ANS如下:

    1
    2
    3
    task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args|
      # You can use args from here
    end

    像这样调用它

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

    如果你不想记住什么是参数位置,你想做一些像Ruby参数散列这样的事情。可以使用一个参数传入一个字符串,然后将该字符串正则化为选项哈希。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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

    在命令行中,你会得到。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ 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
    <div class="
    suo-content">[collapse title=""]<ul><li>您的代码需要一些位置良好的空行。我不知道你是怎么看那面文字墙的。</li></ul>[/collapse]</div><p><center>[wp_ad_camp_4]</center></p><hr>
    <p>
    To run rake tasks with traditional arguments style:
    </p>

    [cc lang="
    ruby"]rake task arg1 arg2

    然后使用:

    1
    2
    3
    task :task do |_, args|
      puts"This is argument 1: #{args.first}"
    end

    添加以下rake gem补丁:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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

    While passing parameters, it is better option is an input file, can this be a excel a json or whatever you need and from there read the data structure and variables you need from that including the variable name as is the need.
    To read a file can have the following structure.

    1
    2
    3
    4
    5
    6
    7
    8
      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

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

    执行

    1
    rake :name_task