Ruby Options parser not reading command line options - ruby

Im trying to use the Ruby builtin options parser
I have this file
File parser.rb
#!/usr/bin/env ruby
require 'optparse'
require 'pp'
class parser
def initialize(args)
#options = Hash.new()
#op = OptionParser.new do |opts|
#options[:verbose] = false
opts.on('-v', '--verbose', 'Output more information') do
#options[:verbose] = true
end
#options[:quick] = false
opts.on( '-q', '--quick', 'Perform the task quickly' ) do
#options[:quick] = true
end
#options[:logfile] = nil
opts.on( '-l', '--logfile FILE', 'Write log to FILE' ) do|file|
#options[:logfile] = file
end
opts.on( '-h', '--help', 'Display this screen' ) do
puts opts
exit
end
#options[:sID] = "-1"
opts.on('-sID', '--senderID', 'Sender ID used by device') do |sID|
#options[:sID] = sID
end
#options[:rID] = "-1"
opts.on('-rID', '--receiverID', 'Receiver ID used by device') do |rID|
#options[:rID] = rID
end
#op.parse!
#op
end
def getOptionsHash
#options
end
then Im trying to use this class in the file below
#!/usr/bin/env ruby
# Setup Bundler
require 'rubygems'
require 'bundler/setup'
require_relative 'parser'
#Variables in the options hash in parser.rb
op = Parser.new(ARGV)
pp op.getOptionsHash()
when I run this on the command line without args it uses default values:
./push_test.rb
I get the following output:
{:verbose=>false,
:quick=>false,
:logfile=>nil,
:sID=>"-1",
:rID=>"-1",
}
when I run this on the command line with args:
./push_test.rb -sID "33"
I get the following output:
{:verbose=>false,
:quick=>false,
:logfile=>nil,
:sID=>"ID",
:rID=>"-1",
}
Why is the sID not being set to 33?
Can anyone help please?Ive tried to figure this out but cant make any headway

Seems the short switch has to be a single character -s
./push_test.rb -sID "33"
outputs:
{:verbose=>false, :quick=>false, :logfile=>nil, :sID=>"ID", :rID=>"-1" }
because everything after -s to the first white space will be assigned to :sID, in your case its the word "ID" that follows "-s", hence you are getting :sID =>"ID"
./push_test.rb -s "33" will do the trick.

From the OptParser docs:
Short style switch:: Specifies short style switch which takes a
mandatory, optional or no argument. It's a string of the following
form:
"-xMANDATORY"
"-x[OPTIONAL]"
"-x"
So at specifying switch -sID you define switch -s with argument named ID - something different than you were probably expecting.

Related

Check command line argument in Ruby code is present

I want to execute Ruby code if command line argument is present:
ruby sanity_checks.rb --trx-request -e staging_psp -g all
Ruby code:
def execute
command_options = CommandOptionsProcessor.parse_command_options
request_type = command_options[:env]
tested_env = command_options[:trx-request] // check here if flag is present
tested_gateways = gateway_names(env: tested_env, gateway_list: command_options[:gateways])
error_logs = []
if(request_type.nil?)
tested_gateways.each do |gateway|
........
end
end
raise error_logs.join("\n") if error_logs.any?
end
How I can get the argument --trx-request and check is it present?
EDIT:
Command parser:
class CommandOptionsProcessor
def self.parse_command_options
command_options = {}
opt_parser = OptionParser.new do |opt|
opt.banner = 'Usage: ruby sanity_checks.rb [OPTIONS]'
opt.separator "Options:"
opt.on('-e TEST_ENV', '--env TEST_ENV','Set tested environment (underscored).') do |setting|
command_options[:env] = setting
end
opt.on('-g X,Y,Z', '--gateways X,Y,Z', Array, 'Set tested gateways (comma separated, no spaces).') do |setting|
command_options[:gateways] = setting
end
end
opt_parser.parse!(ARGV)
command_options
end
end
Can you advice?
You will need to add a boolean switch option to your OptionParser like so
opt.on('-t','--[no-]trx-request','Signifies a TRX Request') do |v|
# note we used an underscore rather than a hyphen to make this symbol
# easier to access
command_options[:trx_request] = v
end
Then in your execute method you can access this as
command_options[:trx_request]
If you need to have a default value you can add one in the parse_command_options method by setting it outside the OptionParser as command_options[:trx_request] = false

Executing program from command line

I have done a program that sends requests to a url and saves them in a file. The program is this, and is working perfectly:
require 'open-uri'
n = gets.to_i
out = gets.chomp
output = File.open( out, "w" )
for i in 1..n
response = open('http://slowapi.com/delay/10').read
output << (response +"\n")
puts response
end
output.close
I want to modify it so that I can execute it from command line. I must run it like this:
fle --test abc -n 300 -f output
What must I do?
Something like this should do the trick:
#!/usr/bin/env ruby
require 'open-uri'
require 'optparse'
# Prepare the parser
options = {}
oparser = OptionParser.new do |opts|
opts.banner = "Usage: fle [options]"
opts.on('-t', '--test [STRING]', 'Test string') { |v| options[:test] = v }
opts.on('-n', '--count COUNT', 'Number of times to send request') { |v| options[:count] = v.to_i }
opts.on('-f', '--file FILE', 'Output file', :REQUIRED) { |v| options[:out_file] = v }
end
# Parse our options
oparser.parse! ARGV
# Check if required options have been filled, print help and exit otherwise.
if options[:count].nil? || options[:out_file].nil?
$stderr.puts oparser.help
exit 1
end
File::open(options[:out_file], 'w') do |output|
options[:count].times do
response = open('http://slowapi.com/delay/10').read
output.puts response # Puts the response into the file
puts response # Puts the response to $stdout
end
end
Here's a more idiomatic way of writing your code:
require 'open-uri'
n = gets.to_i
out = gets.chomp
File.open(out, 'w') do |fo|
n.times do
response = open('http://slowapi.com/delay/10').read
fo.puts response
puts response
end
end
This uses File.open with a block, which allows Ruby to close the file once the block exits. It's a much better practice than assigning the file handle to a variable and use that to close the file later.
How to handle passing in variables from the command-line as options is handled in the other answers.
The first step would be to save you program in a file, add #!/usr/bin/env ruby at the top and chmod +x yourfilename to be able to execute your file.
Now you are able to run your script from the command line.
Secondly, you need to modify your script a little bit to pick up command line arguments. In Ruby, the command line arguments are stored inside ARGV, so something like
ARGV.each do|a|
puts "Argument: #{a}"
end
allows you to retrieve command line arguments.

Optparse doesn't seem to return ARGV array. Argument Required Error

This is homework and I do not expect you to give me the complete answer.
I'm trying to parse a command line entry such as:
./apacheReport.rb -u testlog.txt
When I enter this:
./apacheReport.rb -u testlog.txt
I get:
Argument required
My code is:
require_relative 'CommonLog'
require 'optparse'
# puts ARGV.inspect
optparser = OptionParser.new
optU = false
optI = false
optS = false
optH = false
optparser.banner = "apacheReport.rb [options] filename"
optparser.parse!
rescue => m
puts m.message
puts optparser
exit
end
if ARGV.length < 1
puts "Argument required"
exit
end
userInputFile = ARGV[0]
userInputFile.to_s
file = CommonLog.new(userInputFile)
It should parse the leftover portion of the command into ARGV[0] then should store it as userInputFile and then create a CommonLog object using the file as the constructor. At that point I call the methods that were specified in the command.
It seems that for some reason my ARGV is not being returned. I'm not sure what the issue is.
Ruby's OptionParser is easy to use, but you have to puzzle through the documentation. Here's a little example that'd be useful for your code:
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on('-u', '--use_this FILE', 'Use this file') { |o| options[:use_this] = o }
end.parse!
options will contain the flags. In this case, if you pass in -u foo, options[:use_this] will be foo.
Save that and try running it without and with a parameter. Also try running it with just a -h flag.
You can search StackOverflow for more answers where I was dealing with OptionParser.
It's hard to tell what's wrong since you code doesn't seem to be working at the moment. The problem may be that the parse! method removes found options from ARGV. So when you write:
optparser.parse!
It removes your two parameters (-u testlog.txt) and this code always fails:
if ARGV.length < 1
puts "Argument required"
exit
end
Instead of looking at ARGV, you need to set up optparser correctly. Perhaps something like:
optparser = OptionParser.new do |opts|
opts.banner = "apacheReport.rb [options] filename"
opts.on("-u", "--u-short-for-this", "Whatever u stands for") do |u|
optU = u
end
end
Then optU will be true only if the user passed -u and the filename will be in ARGV[0].

Parse command line arguments in a Ruby script

I want to call a Ruby script from the command line, and pass in parameters that are key/value pairs.
Command line call:
$ ruby my_script.rb --first_name=donald --last_name=knuth
my_script.rb:
puts args.first_name + args.last_name
What is the standard Ruby way to do this? In other languages I usually have to use an option parser. In Ruby I saw we have ARGF.read, but that does not seem to work key/value pairs like in this example.
OptionParser looks promising, but I can't tell if it actually supports this case.
Ruby's built-in OptionParser does this nicely. Combine it with OpenStruct and you're home free:
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on('--first_name FIRSTNAME') { |o| options[:first_name] = o }
opt.on('--last_name LASTNAME') { |o| options[:last_name] = o }
end.parse!
puts options
options will contain the parameters and values as a hash.
Saving and running that at the command line with no parameters results in:
$ ruby test.rb
{}
Running it with parameters:
$ ruby test.rb --first_name=foo --last_name=bar
{:first_name=>"foo", :last_name=>"bar"}
That example is using a Hash to contain the options, but you can use an OpenStruct which will result in usage like your request:
require 'optparse'
require 'ostruct'
options = OpenStruct.new
OptionParser.new do |opt|
opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options.first_name = o }
opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options.last_name = o }
end.parse!
puts options.first_name + ' ' + options.last_name
$ ruby test.rb --first_name=foo --last_name=bar
foo bar
It even automatically creates your -h or --help option:
$ ruby test.rb -h
Usage: test [options]
--first_name FIRSTNAME
--last_name LASTNAME
You can use short flags too:
require 'optparse'
options = {}
OptionParser.new do |opt|
opt.on('-f', '--first_name FIRSTNAME') { |o| options[:first_name] = o }
opt.on('-l', '--last_name LASTNAME') { |o| options[:last_name] = o }
end.parse!
puts options
Running that through its paces:
$ ruby test.rb -h
Usage: test [options]
-f, --first_name FIRSTNAME
-l, --last_name LASTNAME
$ ruby test.rb -f foo --l bar
{:first_name=>"foo", :last_name=>"bar"}
It's easy to add inline explanations for the options too:
OptionParser.new do |opt|
opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options[:first_name] = o }
opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options[:last_name] = o }
end.parse!
and:
$ ruby test.rb -h
Usage: test [options]
-f, --first_name FIRSTNAME The first name
-l, --last_name LASTNAME The last name
OptionParser also supports converting the parameter to a type, such as an Integer or an Array. Refer to the documentation for more examples and information.
You should also look at the related questions list to the right:
"Really Cheap Command-Line Option Parsing in Ruby"
"Pass variables to Ruby script via command line"
Based on the answer by #MartinCortez here's a short one-off that makes a hash of key/value pairs, where the values must be joined with an = sign. It also supports flag arguments without values:
args = Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:=(\S+))?/) ]
…or alternatively…
args = Hash[ ARGV.flat_map{|s| s.scan(/--?([^=\s]+)(?:=(\S+))?/) } ]
Called with -x=foo -h --jim=jam it returns {"x"=>"foo", "h"=>nil, "jim"=>"jam"} so you can do things like:
puts args['jim'] if args.key?('h')
#=> jam
While there are multiple libraries to handle this—including GetoptLong included with Ruby—I personally prefer to roll my own. Here's the pattern I use, which makes it reasonably generic, not tied to a specific usage format, and flexible enough to allow intermixed flags, options, and required arguments in various orders:
USAGE = <<ENDUSAGE
Usage:
docubot [-h] [-v] [create [-s shell] [-f]] directory [-w writer] [-o output_file] [-n] [-l log_file]
ENDUSAGE
HELP = <<ENDHELP
-h, --help Show this help.
-v, --version Show the version number (#{DocuBot::VERSION}).
create Create a starter directory filled with example files;
also copies the template for easy modification, if desired.
-s, --shell The shell to copy from.
Available shells: #{DocuBot::SHELLS.join(', ')}
-f, --force Force create over an existing directory,
deleting any existing files.
-w, --writer The output type to create [Defaults to 'chm']
Available writers: #{DocuBot::Writer::INSTALLED_WRITERS.join(', ')}
-o, --output The file or folder (depending on the writer) to create.
[Default value depends on the writer chosen.]
-n, --nopreview Disable automatic preview of .chm.
-l, --logfile Specify the filename to log to.
ENDHELP
ARGS = { :shell=>'default', :writer=>'chm' } # Setting default values
UNFLAGGED_ARGS = [ :directory ] # Bare arguments (no flag)
next_arg = UNFLAGGED_ARGS.first
ARGV.each do |arg|
case arg
when '-h','--help' then ARGS[:help] = true
when 'create' then ARGS[:create] = true
when '-f','--force' then ARGS[:force] = true
when '-n','--nopreview' then ARGS[:nopreview] = true
when '-v','--version' then ARGS[:version] = true
when '-s','--shell' then next_arg = :shell
when '-w','--writer' then next_arg = :writer
when '-o','--output' then next_arg = :output
when '-l','--logfile' then next_arg = :logfile
else
if next_arg
ARGS[next_arg] = arg
UNFLAGGED_ARGS.delete( next_arg )
end
next_arg = UNFLAGGED_ARGS.first
end
end
puts "DocuBot v#{DocuBot::VERSION}" if ARGS[:version]
if ARGS[:help] or !ARGS[:directory]
puts USAGE unless ARGS[:version]
puts HELP if ARGS[:help]
exit
end
if ARGS[:logfile]
$stdout.reopen( ARGS[:logfile], "w" )
$stdout.sync = true
$stderr.reopen( $stdout )
end
# etc.
There is a number of command line arguments parsers in Ruby:
GetoptLong - Included in stdlib
OptionParser - No longer part of stdlib, since Ruby 3.0.0 converted to a separate optparse gem
slop
optimist
and many more...
Personally I'd choose slop or optimist, those are not part of standard Ruby installation.
gem install slop
But it offers simplicity and code readability. Assuming slightly more complex example with required arguments and default values:
require 'slop'
begin
opts = Slop.parse do |o|
o.int '-a', '--age', 'Current age', default: 42
o.string '-f', '--first_name', 'The first name', required: true
o.string '-l', '--last_name', 'The last name', required: true
o.bool '-v', '--verbose', 'verbose output', default: false
o.on '-h','--help', 'print the help' do
puts o
exit
end
end
p opts.to_hash
rescue Slop::Error => e
puts e.message
end
optimist formerly known as trollop, it's very easy to ready, with minimum boilerplate code:
gem install optimist
require 'optimist'
opts = Optimist::options do
opt :verbose, "verbose mode"
opt :first_name, "The first name", type: :string, required: true
opt :last_name, "The last name", type: :string, required: true
opt :age, "Current age", default: 42
end
p opts
Similar example using OptionParser:
#!/usr/bin/env ruby
require 'optparse'
require 'ostruct'
begin
options = OpenStruct.new
OptionParser.new do |opt|
opt.on('-a', '--age AGE', 'Current age') { |o| options.age = o }
opt.on('-f', '--first_name FIRSTNAME', 'The first name') { |o| options.first_name = o }
opt.on('-l', '--last_name LASTNAME', 'The last name') { |o| options.last_name = o }
opt.on('-v', '--verbose', 'Verbose output') { |o| options.verbose = true }
end.parse!
options[:age] = 42 if options[:age].nil?
raise OptionParser::MissingArgument.new('--first_name') if options[:first_name].nil?
raise OptionParser::MissingArgument.new('--last_name') if options[:last_name].nil?
options[:verbose] = false if options[:verbose].nil?
rescue OptionParser::ParseError => e
puts e.message
exit
end
GetoptLong parsing is even more complicated:
require 'getoptlong'
opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--first_name', '-f', GetoptLong::REQUIRED_ARGUMENT ],
[ '--last_name', '-l', GetoptLong::REQUIRED_ARGUMENT ],
[ '--age','-a', GetoptLong::OPTIONAL_ARGUMENT ],
[ '--verbose','-v', GetoptLong::OPTIONAL_ARGUMENT ]
)
begin
options = {}
options[:verbose] = false
options[:age] = 42
opts.each do |opt, arg|
case opt
when '--help'
puts <<-EOF
usage: ./getlongopts.rb [options]
-a, --age Current age
-f, --first_name The first name
-l, --last_name The last name
-v, --verbose verbose output
-h, --help print the help
EOF
when '--first_name'
options[:first_name] = arg
when '--last_name'
options[:last_name] = arg
when '--age'
options[:age] = arg.to_i
when '--verbose'
options[:verbose] = arg
else
puts "unknown option `#{opt}`"
exit 1
end
end
raise GetoptLong::MissingArgument.new('Missing argument --first_name') if options[:first_name].nil?
raise GetoptLong::MissingArgument.new('Missing argument --last_name') if options[:last_name].nil?
rescue GetoptLong::Error => e
puts e.message
exit
end
puts options
Command line arguments was never meant to be a rocket science task, spend your time on reading/writing more useful code :)
I personally use Docopt. This is much more clear, maintainable and easy to read.
Have a look at the Ruby implementation's documentation for examples. The usage is really straightforward.
gem install docopt
Ruby code:
doc = <<DOCOPT
My program who says hello
Usage:
#{__FILE__} --first_name=<first_name> --last_name=<last_name>
DOCOPT
begin
args = Docopt::docopt(doc)
rescue Docopt::Exit => e
puts e.message
exit
end
print "Hello #{args['--first_name']} #{args['--last_name']}"
Then calling:
$ ./says_hello.rb --first_name=Homer --last_name=Simpsons
Hello Homer Simpsons
And without arguments:
$ ./says_hello.rb
Usage:
says_hello.rb --first_name=<first_name> --last_name=<last_name>
A bit of standard Ruby Regexp in myscript.rb:
args = {}
ARGV.each do |arg|
match = /--(?<key>.*?)=(?<value>.*)/.match(arg)
args[match[:key]] = match[:value] # e.g. args['first_name'] = 'donald'
end
puts args['first_name'] + ' ' + args['last_name']
And on the command line:
$ ruby script.rb --first_name=donald --last_name=knuth
Produces:
$ donald knuth
Here is a slight modification to #Phrogz excellent answer: this mod will allow you to pass a string with spaces in it.
args= Hash[ ARGV.join(' ').scan(/--?([^=\s]+)(?:="(.*?)"+)?/)]
In a command line pass the string like this:
ruby my_script.rb '--first="Boo Boo" --last="Bear"'
Or from another ruby script like this:
system('ruby my_script.rb \'--first="Boo Boo" --last="Bear"\'')
Results:
{"first"=>"Boo Boo", "last"=>"Bear"}
An improved version that handles arguments that are not options, arguments with a parameter, and -a as well as --a.
def parse(args)
parsed = {}
args.each do |arg|
match = /^-?-(?<key>.*?)(=(?<value>.*)|)$/.match(arg)
if match
parsed[match[:key].to_sym] = match[:value]
else
parsed[:text] = "#{parsed[:text]} #{arg}".strip
end
end
parsed
end

Command line option parsing in Ruby

I have a ruby script that parses command line options given to it as follows:
#!/usr/bin/ruby
require 'optparse'
puts 'Hello World!, This is my first ruby program'
options = {}
optparse = OptionParser.new do|opts|
opts.banner = "Featbuild minimal trial script for command line parsing"
options[:cpl] = nil
opts.on('-cpl SWITCH_STATE', 'compile on or off') do|cplopt|
options[:cpl] = cplopt
OPT_CPL=cplopt
puts cplopt
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
optparse.parse!
output = open("mypipe", "w+")
output.puts OPT_CPL
#output.flush
Now the line opts.on('-cpl SWITCH_STATE', 'compile on or off') do|cplopt| in the above script is where I have a problem.
I believe we can do it in follwoing ways:
1.)opts.on('--cpl SWITCH_STATE', 'compile on or off') do|cplopt|
2.)opts.on('-c', '--cpl SWITCH_STATE', 'compile on or off') do|cplopt|
3.)opts.on('-cpl SWITCH_STATE', 'compile on or off') do|cplopt|
This is what I pass as the arguments that works:
$./try1.rb --cpl on
$./try1.rb -c on
This does not work:
$./try1.rb -cpl on
Ruby, instead of getting 'on' as the option argument, gets 'pl', as if $./try.rb -c pl was specified.
I want to have the string $./try1.rb -cpl on be parsed in such a way that 'on' gets passed to the block of the method opts.on() in 'cplopt'.
I was referring to this tutorial: http://ruby.about.com/od/advancedruby/a/optionparser2.htm
It seems '-cpl on' isn't possible in Ruby? Is this so?
What other alternatve solutions can I apply over here?
Try Trollop, as it makes option parsing life easier.
require 'trollop'
opts = Trollop::options do
version "compile 0.1.0"
banner "Usage: compile <option> - where [options] are:"
opt :cpl, "compile on or off", :type => :string, :default => "off"
end
puts opts.cpl
When run, results in:
$ ruby ./trollop.rb --cpl on
on
$ ruby ./trollop.rb --cpl off
off
$ ruby ./trollop.rb -c on
on
$ ruby ./trollop.rb -c off
off
$ ruby ./trollop.rb
off
Trollop 2.0 supports no- negation of boolean options which you might find easier than dealing with the on/off strings.
opt "cpl", "Compile", :default => true
When run, results in:
$ ruby trollop.rb --cpl
true
$ ruby trollop.rb --no-cpl
false
I think you will need to ensure that only cp1 is in the single quotes instead of
-cpl SWITCH_STATE
do
opts.on('-cpl', 'compile on or off') do|cplopt|
options[:cpl] = cplopt
OPT_CPL=cplopt
puts cplopt
end
Here is an example:
opts.on('-s', '--size 1G or 1024M', '1G or 1024M') do |s|
options[:size] = s;
end

Resources