304

I've installed RubyInstaller on Windows and I'm running IMAP Sync but I need to use it to sync hundreds of accounts. If I could pass these variables to it via command line I could automate the whole process better.

# Source server connection info.
SOURCE_NAME = '[email protected]'
SOURCE_HOST = 'mail.example.com'
SOURCE_PORT = 143
SOURCE_SSL  = false
SOURCE_USER = 'username'
SOURCE_PASS = 'password'

# Destination server connection info.
DEST_NAME = '[email protected]'
DEST_HOST = 'imap.gmail.com'
DEST_PORT = 993
DEST_SSL  = true
DEST_USER = '[email protected]'
DEST_PASS = 'password'
1
  • 2
    You may want to consider editing this popular question into an actual question.
    – not2qubit
    Commented Sep 17, 2018 at 10:22

8 Answers 8

513

Something like this:

ARGV.each do|a|
  puts "Argument: #{a}"
end

then

$ ./test.rb "test1 test2"

or

v1 = ARGV[0]
v2 = ARGV[1]
puts v1       #prints test1
puts v2       #prints test2
3
  • 91
    I would like to explicitly point out that ARGV[0] does not point to the program name, as some other languages do. To get the program name, see stackoverflow.com/questions/4834821/… Commented May 1, 2015 at 18:37
  • 8
    Isn't `"test1 test2" just a single argument?
    – wuliwong
    Commented Jun 9, 2019 at 22:52
  • 2
    You need to add #!/usr/bin/env ruby on top of .rb file to be able to run it like this: ./test.rb
    – xamenrax
    Commented Sep 15, 2019 at 13:42
208

Don't reinvent the wheel; check out Ruby's way-cool OptionParser library.

It offers parsing of flags/switches, parameters with optional or required values, can parse lists of parameters into a single option and can generate your help for you.

Also, if any of your information being passed in is pretty static, that doesn't change between runs, put it into a YAML file that gets parsed. That way you can have things that change every time on the command-line, and things that change occasionally configured outside your code. That separation of data and code is nice for maintenance.

Here are some samples to play with:

require 'optparse'
require 'yaml'

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: example.rb [options]"

  opts.on('-n', '--sourcename NAME', 'Source name') { |v| options[:source_name] = v }
  opts.on('-h', '--sourcehost HOST', 'Source host') { |v| options[:source_host] = v }
  opts.on('-p', '--sourceport PORT', 'Source port') { |v| options[:source_port] = v }

end.parse!

dest_options = YAML.load_file('destination_config.yaml')
puts dest_options['dest_name']

This is a sample YAML file if your destinations are pretty static:

--- 
dest_name: [email protected]
dest_host: imap.gmail.com
dest_port: 993
dest_ssl: true
dest_user: [email protected]
dest_pass: password

This will let you easily generate a YAML file:

require 'yaml'

yaml = {
  'dest_name' => '[email protected]',
  'dest_host' => 'imap.gmail.com',
  'dest_port' => 993,
  'dest_ssl'  => true,
  'dest_user' => '[email protected]',
  'dest_pass' => 'password'
}

puts YAML.dump(yaml)
2
  • 2
    OptParse link is dead. Try ruby-doc.org/stdlib-1.9.3/libdoc/optparse/rdoc/…
    – Casey
    Commented Aug 23, 2012 at 17:55
  • 7
    Excellent answer; may be worth adding that after option-parsing is done, ARGV contains only the operands, if any (that is, the remaining, NON-option arguments).
    – mklement0
    Commented Jul 13, 2015 at 22:07
30

Unfortunately, Ruby does not support such passing mechanism as e.g. AWK:

> awk -v a=1 'BEGIN {print a}'
> 1

It means you cannot pass named values into your script directly.

Using cmd options may help:

> ruby script.rb val_0 val_1 val_2

# script.rb
puts ARGV[0] # => val_0
puts ARGV[1] # => val_1
puts ARGV[2] # => val_2

Ruby stores all cmd arguments in the ARGV array, the scriptname itself can be captured using the $PROGRAM_NAME variable.

The obvious disadvantage is that you depend on the order of values.

If you need only Boolean switches use the option -s of the Ruby interpreter:

> ruby -s -e 'puts "So do I!" if $agreed' -- -agreed
> So do I!

Please note the -- switch, otherwise Ruby will complain about a nonexistent option -agreed, so pass it as a switch to your cmd invokation. You don't need it in the following case:

> ruby -s script_with_switches.rb -agreed
> So do I!

The disadvantage is that you mess with global variables and have only logical true/false values.

You can access values from environment variables:

> FIRST_NAME='Andy Warhol' ruby -e 'puts ENV["FIRST_NAME"]'
> Andy Warhol

Drawbacks are present here to, you have to set all the variables before the script invocation (only for your ruby process) or to export them (shells like BASH):

> export FIRST_NAME='Andy Warhol'
> ruby -e 'puts ENV["FIRST_NAME"]'

In the latter case, your data will be readable for everybody in the same shell session and for all subprocesses, which can be a serious security implication.

And at least you can implement an option parser using getoptlong and optparse.

Happy hacking!

0
3

tl;dr

I know this is old, but getoptlong wasn't mentioned here and it's probably the best way to parse command line arguments today.


Parsing command line arguments

I strongly recommend getoptlong. It's pretty easy to use and works like a charm. Here is an example extracted from the link above

require 'getoptlong'

opts = GetoptLong.new(
    [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
    [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--name', GetoptLong::OPTIONAL_ARGUMENT ]
)

dir = nil
name = nil
repetitions = 1
opts.each do |opt, arg|
    case opt
        when '--help'
            puts <<-EOF
hello [OPTION] ... DIR

-h, --help:
     show help

--repeat x, -n x:
     repeat x times

--name [name]:
     greet user by name, if name not supplied default is John

DIR: The directory in which to issue the greeting.
            EOF
        when '--repeat'
            repetitions = arg.to_i
        when '--name'
            if arg == ''
                name = 'John'
            else
                name = arg
            end
    end
end

if ARGV.length != 1
    puts "Missing dir argument (try --help)"
    exit 0
end

dir = ARGV.shift

Dir.chdir(dir)
for i in (1..repetitions)
    print "Hello"
    if name
        print ", #{name}"
    end
    puts
end

You can call it like this ruby hello.rb -n 6 --name -- /tmp

What OP is trying to do

In this case I think the best option is to use YAML files as suggested in this answer

1

You can also try out cliqr. Its pretty new and in active development. But there are stable releases ready to be used. Here is the git repo: https://github.com/anshulverma/cliqr

Look into the example folder to get an idea on how it can be used.

0
0

Run this code on the command line and enter the value of N:

N  = gets; 1.step(N.to_i, 1) { |i| print "hello world\n" }
0

Unless it is the most trivial case, there is only one sane way to use command line options in Ruby. It is called docopt and documented here.

What is amazing with it, is it's simplicity. All you have to do, is specify the "help" text for your command. What you write there will then be auto-parsed by the standalone (!) ruby library.

From the example:

#!/usr/bin/env ruby
require 'docopt.rb'

doc = <<DOCOPT
Usage: #{__FILE__} --help
       #{__FILE__} -v...
       #{__FILE__} go [go]
       #{__FILE__} (--path=<path>)...
       #{__FILE__} <file> <file>

Try: #{__FILE__} -vvvvvvvvvv
     #{__FILE__} go go
     #{__FILE__} --path ./here --path ./there
     #{__FILE__} this.txt that.txt

DOCOPT

begin
  require "pp"
  pp Docopt::docopt(doc)
rescue Docopt::Exit => e
  puts e.message
end

The output:

$ ./counted_example.rb -h
Usage: ./counted_example.rb --help
       ./counted_example.rb -v...
       ./counted_example.rb go [go]
       ./counted_example.rb (--path=<path>)...
       ./counted_example.rb <file> <file>

Try: ./counted_example.rb -vvvvvvvvvv
     ./counted_example.rb go go
     ./counted_example.rb --path ./here --path ./there
     ./counted_example.rb this.txt that.txt

$ ./counted_example.rb something else
{"--help"=>false,
 "-v"=>0,
 "go"=>0,
 "--path"=>[],
 "<file>"=>["something", "else"]}

$ ./counted_example.rb -v
{"--help"=>false, "-v"=>1, "go"=>0, "--path"=>[], "<file>"=>[]}

$ ./counted_example.rb go go
{"--help"=>false, "-v"=>0, "go"=>2, "--path"=>[], "<file>"=>[]}

Enjoy!

0

You should try console_runner gem. This gem makes your pure Ruby code executable from command-line. All you need is to add YARD annotations to your code:

# @runnable This tool can talk to you. Run it when you are lonely.
#   Written in Ruby.  
class MyClass

    def initialize
      @hello_msg = 'Hello' 
      @bye_msg = 'Good Bye' 
    end

    # @runnable Say 'Hello' to you.
    # @param [String] name Your name
    # @param [Hash] options options
    # @option options [Boolean] :second_meet Have you met before?
    # @option options [String] :prefix Your custom prefix
    def say_hello(name, options = {})
      second_meet = nil
      second_meet = 'Nice to see you again!' if options['second_meet']
      prefix = options['prefix']
      message = @hello_msg + ', '
      message += "#{prefix} " if prefix
      message += "#{name}. "
      message += second_meet if second_meet
      puts message
    end

end

Then run it from console:

$ c_run /projects/example/my_class.rb  say_hello -n John --second-meet --prefix Mr. 
-> Hello, Mr. John. Nice to see you again!

Not the answer you're looking for? Browse other questions tagged or ask your own question.