How can I use a comma in a string argument to a rake task?
Asked Answered
D

9

22

I have the following Rakefile:

task :test_commas, :arg1 do |t, args|
  puts args[:arg1]
end

And want to call it with a single string argument containing commas. Here's what I get:

%rake 'test_commas[foo, bar]'
foo

%rake 'test_commas["foo, bar"]'
"foo

%rake "test_commas['foo, bar']"
'foo

%rake "test_commas['foo,bar']"
'foo

%rake "test_commas[foo\,bar]"
foo\

I'm currently using the workaround proposed in this pull request to rake, but is there a way to accomplish this without patching rake?

Donny answered 31/8, 2011 at 14:6 Comment(0)
C
9

I'm not sure it's possible. Looking at lib/rake/application.rb, the method for parsing the task string is:

def parse_task_string(string)
  if string =~ /^([^\[]+)(\[(.*)\])$/
    name = $1
    args = $3.split(/\s*,\s*/)
  else
    name = string
    args = []
  end 
  [name, args]
end 

It appears that the arguments string is split by commas, so you cannot have an argument that contains a comma, at least not in the current rake-0.9.2.

Consultation answered 31/8, 2011 at 15:29 Comment(1)
This is no longer the case. Commas can now be escaped with a \Bois
T
10

Changing rake is quite a dirty fix. Try using OptionParser instead. The syntax is following

$ rake mytask -- -s 'some,comma,sepparated,string'

the -- is necessary to skip the rake way of parsing the arguments

and here's the ruby code:

task :mytask do
  options = {}

  optparse = OptionParser.new do |opts|
    opts.on('-s', '--string ARG', 'desc of my argument') do |str|
      options[:str] = str
    end

    opts.on('-h', '--help', 'Display this screen') do     
      puts opts                                                          
      exit                                                                      
    end 
  end

  begin 
    optparse.parse!
    mandatory = [:str]
    missing = mandatory.select{ |param| options[param].nil? }
    if not missing.empty?
      puts "Missing options: #{missing.join(', ')}"
      puts optparse
      exit
    end

  rescue OptionParser::InvalidOption, OptionParser::MissingArgument
    puts $!.to_s
    puts optparse
    exit  
  end

  puts "Performing task with options: #{options.inspect}"
  # @TODO add task's code
end
Toniatonic answered 18/7, 2012 at 12:4 Comment(1)
This is a really clean approach from an implementation standpoint. From a usability standpoint, I'm used to using -- to separate options from arguments, especially with git, so it feels weird to have to use -- and then treat the arguments as options. But then again if I cared about usability, I'd probably just go with a custom CLI or use thor :)Donny
C
9

I'm not sure it's possible. Looking at lib/rake/application.rb, the method for parsing the task string is:

def parse_task_string(string)
  if string =~ /^([^\[]+)(\[(.*)\])$/
    name = $1
    args = $3.split(/\s*,\s*/)
  else
    name = string
    args = []
  end 
  [name, args]
end 

It appears that the arguments string is split by commas, so you cannot have an argument that contains a comma, at least not in the current rake-0.9.2.

Consultation answered 31/8, 2011 at 15:29 Comment(1)
This is no longer the case. Commas can now be escaped with a \Bois
M
4

Have you tried escaping the , with a \?

Matriculate answered 31/8, 2011 at 14:12 Comment(2)
yes, and that doesn't work for me either (in any of the cases above). I'll update the question with an example of that.Donny
Worked like a charm!Tedious
H
4

Eugen already answered, why it doesn't work.

But perhaps the following workaround may help you:

task :test_commas, :arg1, :arg2 do |t, args|
  arg = args.to_hash.values.join(',')
  puts "Argument is #{arg.inspect}"
end

It takes two arguments, but joins them to get the 'real' one.

If you have more then one comma, you need more arguments.


I did some deeper research and found one (or two) solution. I don't think it's a perfect solution, but it seems it works.

require 'rake'
module Rake
  class Application
    #usage: 
    #   rake test_commas[1\,2\,3]
    def parse_task_string_masked_commas(string)
      if string =~ /^([^\[]+)(\[(.*)\])$/
        name = $1
        args = $3.split(/\s*(?<!\\),\s*/).map{|x|x.gsub(/\\,/,',')}
      else
        name = string
        args = []
      end 
      [name, args]
    end   

    #Usage: 
    #   rake test_commas[\"1,2\",3]
    #" and ' must be masked
    def parse_task_string_combined(string)
      if string =~ /^([^\[]+)(\[(.*)\])$/
        name = $1
        args = $3.split(/(['"].+?["'])?,(["'].+?["'])?/).map{|ts| ts.gsub(/\A["']|["']\Z/,'') }
        args.shift if args.first.empty?
      else
        name = string
        args = []
      end 
      [name, args]
    end   

    #~ alias :parse_task_string :parse_task_string_masked_commas
    alias :parse_task_string :parse_task_string_combined

  end

end
desc 'Test comma separated arguments'
task :test_commas, :arg1  do |t, args|
  puts '---'
  puts "Argument is #{args.inspect}"
end

The version parse_task_string_masked_commasallows calls with masked commas:

rake test_commas[1\,2\,3]

The version parse_task_string_combined allows:

rake test_commas[\"1,2,3\"]

At least under windows, the " (or ') must be masked. If not, they are already deleted until the string reached Rake::Aplication (probably shell substitution)

Havoc answered 31/8, 2011 at 19:10 Comment(8)
That's a good workaround if I know the input format beforehand, but in that case I could just name the arguments so rake -T would be more descriptive.Donny
I added a new version - it's a bit nearer your proposals.Havoc
This would be worth submitting as an upstream patch, so that you can include commas in strings. What about using a regex with negative lookbehind (matching a comma that isn't preceded by a slash) instead of using MYSEP?Donny
I made some experiments with regexps, but I didn't have success. Then I was too tired to continue... I will take a 2nd look for it.Havoc
I replaced MYSEP at least for the maked comma version. For parameters in " I have actual no better idea.Havoc
I have now a solution without the MYSEP-replacements.Havoc
I like the version with escaping the quotes the best, although it's still not ideal, as escaping quotes usually means I want a literal quote, where here it's just a workaround to get the quotes far enough into the parsing so we can deal with them. When I try running this (on osx) using ruby-1.9.2-p290 I can't get it to output commas in the string. The output is Argument is {:arg1=>"1"} even when I escape the quotes.Donny
Use args.to_a instead of args.to_hash.values. Hash data structures don't preserve order.Mansoor
J
3

Rake task variables are not very convenient generally, instead use environment variables:

task :my_task do
  ENV["MY_ARGUMENT"].split(",").each_with_index do |arg, i|
    puts "Argument #{i}: #{arg}"
  end
end

Then you can invoke with

MY_ARGUMENT=foo,bar rake my_task

Or if you prefer

rake my_task MY_ARGUMENT=foo,bar
Joiner answered 22/7, 2015 at 16:57 Comment(1)
Thanks! All the options presented here are a bit dirty and/or ugly, but this was by far the quickest.Packard
C
3

For rake version 12.3.2,

rake test_commas["foo\,bar"]

works well

Clathrate answered 18/9, 2019 at 17:49 Comment(0)
W
2

Another simple workaround is to use a different delimiter in your arguments.

It's pretty simple to swap to a pipe | character (or another) instead of your comma separated args. Rake parses your arguments correctly in this case and allows you to split the first for an array.

desc 'Test pipe separated arguments'
task :test_pipes, :arg1, :arg2  do |t, args|
  puts ":arg1 is: #{ args[:arg1] }"
  puts ":arg2 still works: #{ args[:arg2] }"
  puts "Split :arg1 is: #{ args[:arg1].split('|') }"
end

Call it with:

rake test_pipes["foo|bar",baz]
Wayfaring answered 20/5, 2014 at 16:49 Comment(0)
H
2

# rake -v 10.5

rake test_commas\['foo\,bar'\]

works like a charm.

Honky answered 13/6, 2017 at 14:49 Comment(0)
H
0

We can convert each string to symbol, As,

task :create do   
ARGV.each { |a| task a.to_sym do ; end }
 puts ARGV[1]
 puts ARGV[2]
 puts ARGV[3]
 puts ARGV[4]     
end
end

run as,

rake create '1,s' '2' 'ww ww' 'w,w'

Hertfordshire answered 9/12, 2016 at 7:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.