[ruby] How to pass command line arguments to a rake task

I have a rake task that needs to insert a value into multiple databases.

I'd like to pass this value into the rake task from the command line, or from another rake task.

How can I do this?

This question is related to ruby command-line rake command-line-arguments

The answer is


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.

  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

Example json

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

Execution

rake :name_task 

I use a regular ruby argument in the rake file:

DB = ARGV[1]

then I stub out the rake tasks at the bottom of the file (since rake will look for a task based on that argument name).

task :database_name1
task :database_name2

command line:

rake mytask db_name

this feels cleaner to me than the var=foo ENV var and the task args[blah, blah2] solutions.
the stub is a little jenky, but not too bad if you just have a few environments that are a one-time setup


The ways to pass argument are correct in above answer. However to run rake task with arguments, there is a small technicality involved in newer version of rails

It will work with rake "namespace:taskname['argument1']"

Note the Inverted quotes in running the task from command line.


To run rake tasks with traditional arguments style:

rake task arg1 arg2

And then use:

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

Add following patch of 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

One thing I don't see here is how to handle arbitrary arguments. If you pass arguments that are not listed in the task definition, they are still accessible under args.extras:

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

If you want to pass named arguments (e.g. with standard OptionParser) you could use something like this:

$ rake user:create -- --user [email protected] --pass 123

note the --, that's necessary for bypassing standard Rake arguments. Should work with Rake 0.9.x, <= 10.3.x.

Newer Rake has changed its parsing of --, and now you have to make sure it's not passed to the OptionParser#parse method, for example with 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 at the end will make sure that the extra arguments won't be interpreted as Rake task.

Also the shortcut for arguments should work:

 rake user:create -- -u [email protected] -p 123

When rake scripts look like this, maybe it's time to look for another tool that would allow this just out of box.


You can specify formal arguments in rake by adding symbol arguments to the task call. For example:

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

Then, from the command line:

> rake my_task[1,false]
Args were: {:arg1=>"1", :arg2=>"false"} of class Rake::TaskArguments
arg1 was: '1' of class String
arg2 was: 'false' of class String

> rake "my_task[1, 2]"
Args were: {:arg1=>"1", :arg2=>"2"}

> rake invoke_my_task
Args were: {:arg1=>"1", :arg2=>"2"}

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

> rake with_prerequisite[5,6]
Args were: {:arg1=>"5", :arg2=>"6"}

> rake with_defaults
Args with defaults were: {:arg1=>:default_1, :arg2=>:default_2}

> rake with_defaults['x','y']
Args with defaults were: {:arg1=>"x", :arg2=>"y"}

As demonstrated in the second example, if you want to use spaces, the quotes around the target name are necessary to keep the shell from splitting up the arguments at the space.

Looking at the code in rake.rb, it appears that rake does not parse task strings to extract arguments for prerequisites, so you can't do task :t1 => "dep[1,2]". The only way to specify different arguments for a prerequisite would be to invoke it explicitly within the dependent task action, as in :invoke_my_task and :invoke_my_task_2.

Note that some shells (like zsh) require you to escape the brackets: rake my_task\['arg1'\]


Another commonly used option is to pass environment variables. In your code you read them via ENV['VAR'], and can pass them right before the rake command, like

$ VAR=foo rake mytask

Actually @Nick Desjardins answered perfect. But just for education: you can use dirty approach: using ENV argument

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

rake my_task myvar=10
#=> myvar: 10

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:

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

To pass arguments, do three things:

  1. Add the argument names after the task name, separated by commas.
  2. Put the dependencies at the end using :needs => [...]
  3. Place |t, args| after the do. (t is the object for this task)

To access the arguments in the script, use 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

To call this task from the command line, pass it the arguments in []s

rake task_name['Hello',4]

will output

Hello
Hello
Hello
Hello

and if you want to call this task from another task, and pass it arguments, use invoke

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

then the command

rake caller

will output

In Caller
hi
hi

I haven't found a way to pass arguments as part of a dependency, as the following code breaks:

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

Most of the methods described above did not work for me, maybe they are deprecated in the newer versions. The up-to-date guide can be found here: http://guides.rubyonrails.org/command_line.html#custom-rake-tasks

a copy-and-paste ans from the guide is here:

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

Invoke it like this

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

If you can't be bothered to remember what argument position is for what and you want do something like a ruby argument hash. You can use one argument to pass in a string and then regex that string into an options hash.

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

And on the command line you get.

$ 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

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

To pass arguments to the default task, you can do something like this. For example, say "version" is your argument:

task :default, [:version] => [:build]

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

Then you can call it like so:

$ rake
no version passed

or

$ rake default[3.2.1]
version is 3.2.1

or

$ rake build[3.2.1]
version is 3.2.1

However, I have not found a way to avoid specifying the task name (default or build) while passing in arguments. Would love to hear if anyone knows of a way.


I couldn't figure out how to pass args and also the :environment until I worked this out:

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

And then I call like this:

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

I like the "querystring" syntax for argument passing, especially when there are a lot of arguments to be passed.

Example:

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

The "querystring" being:

width=10&height=20

Warning: note that the syntax is rake "mytask[foo=bar]" and NOT rake mytask["foo=bar"]

When parsed inside the rake task using Rack::Utils.parse_nested_query , we get a Hash:

=> {"width"=>"10", "height"=>"20"}

(The cool thing is that you can pass hashes and arrays, more below)

This is how to achieve this:

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

Here's a more extended example that I'm using with Rails in my delayed_job_active_record_threaded gem:

bundle exec rake "dj:start[ebooks[workers_number]=16&ebooks[worker_timeout]=60&albums[workers_number]=32&albums[worker_timeout]=120]"

Parsed the same way as above, with an environment dependency (in order load the Rails environment)

namespace :dj do
  task :start, [ :args_expr ] => :environment do |t, args|
    # defaults here...
    options = Rack::Utils.parse_nested_query(args[:args_expr])  
  end
end

Gives the following in options

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

I just wanted to be able to run:

$ rake some:task arg1 arg2

Simple, right? (Nope!)

Rake interprets arg1 and arg2 as tasks, and tries to run them. So we just abort before it does.

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

    # your task...

    exit
  end
end

Take that, brackets!

Disclaimer: I wanted to be able to do this in a pretty small pet project. Not intended for "real world" usage since you lose the ability to chain rake tasks (i.e. rake task1 task2 task3). IMO not worth it. Just use the ugly rake task[arg1,arg2].


In addition to answer by kch (I didn't find how to leave a comment to that, sorry):

You don't have to specify variables as ENV variables before the rake command. You can just set them as usual command line parameters like that:

rake mytask var=foo

and access those from your rake file as ENV variables like such:

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

Examples related to ruby

Uninitialized Constant MessagesController Embed ruby within URL : Middleman Blog Titlecase all entries into a form_for text field Ruby - ignore "exit" in code Empty brackets '[]' appearing when using .where find_spec_for_exe': can't find gem bundler (>= 0.a) (Gem::GemNotFoundException) How to update Ruby Version 2.0.0 to the latest version in Mac OSX Yosemite? How to fix "Your Ruby version is 2.3.0, but your Gemfile specified 2.2.5" while server starting Is the server running on host "localhost" (::1) and accepting TCP/IP connections on port 5432? How to update Ruby with Homebrew?

Examples related to command-line

Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) Flutter command not found Angular - ng: command not found how to run python files in windows command prompt? How to run .NET Core console app from the command line Copy Paste in Bash on Ubuntu on Windows How to find which version of TensorFlow is installed in my system? How to install JQ on Mac by command-line? Python not working in the command line of git bash Run function in script from command line (Node JS)

Examples related to rake

Bundler::GemNotFound: Could not find rake-10.3.2 in any of the sources Difference between rake db:migrate db:reset and db:schema:load Rails and PostgreSQL: Role postgres does not exist What does bundle exec rake mean? "Could not find bundler" error Rails how to run rake task How to rollback just one step using rake db:migrate Purge or recreate a Ruby on Rails database Rails DB Migration - How To Drop a Table? how to do "press enter to exit" in batch

Examples related to command-line-arguments

How to pass arguments to Shell Script through docker run How can I pass variable to ansible playbook in the command line? Use Robocopy to copy only changed files? mkdir's "-p" option What is Robocopy's "restartable" option? How do you run a .exe with parameters using vba's shell()? Bash command line and input limit Check number of arguments passed to a Bash script Parsing boolean values with argparse Import SQL file by command line in Windows 7