Pass variables to Ruby script via command line

RubyCommand Line

Ruby Problem Overview


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'

Ruby Solutions


Solution 1 - Ruby

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

Solution 2 - Ruby

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)

Solution 3 - Ruby

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!

Solution 4 - Ruby

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.

Solution 5 - Ruby

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

Solution 6 - Ruby

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" }

Solution 7 - Ruby

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!

Solution 8 - Ruby

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!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRobin RodricksView Question on Stackoverflow
Solution 1 - RubycethView Answer on Stackoverflow
Solution 2 - Rubythe Tin ManView Answer on Stackoverflow
Solution 3 - RubyAndrei BeliankouView Answer on Stackoverflow
Solution 4 - RubynuaaveeView Answer on Stackoverflow
Solution 5 - RubyRedithionView Answer on Stackoverflow
Solution 6 - Rubytin tinView Answer on Stackoverflow
Solution 7 - Rubynot2qubitView Answer on Stackoverflow
Solution 8 - RubyYuri KarpovichView Answer on Stackoverflow