Check command line argument in Ruby code is present - ruby

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

Related

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].

How do I use command-line options in Ruby?

How do I use command options in scripts?
I have a script that connects to a device, runs commands and prints the output. I would like to use a file for the hosts, and one for the user and passwd info. When I try to run the script I get these errors:
ruby threat_detection.rb --host_file=hosts --config_file=config
threat_detection.rb:61:in '<main>': needless argument: --host_file=hosts (OptionParser::NeedlessArgument)
ruby threat_detection.rb '--host_file=hosts' '--config_file=config'
threat_detection.rb:61:in '<main>': needless argument: --host_file=hosts (OptionParser::NeedlessArgument)
ruby threat_detection.rb --host_file-hosts --config_file-config
threat_detection.rb:61:in '<main>': invalid option: --host_file-hosts (OptionParser::InvalidOption)
This is the code:
options ={}
opt_parser = OptionParser.new do |opt|
opt.banner = 'Usage: opt_parser COMMAND [OPTIONS]'
opt.on('--host_file', 'I need hosts, put them here') do |host_file|
options[:host_file] == host_file
end
opt.on('--config_file', 'I need config info, put it here') do |config_file|
options[:config_file] == config_file
end
opt.on('-h', '--help', 'What your looking at') do |help|
options[:help] == help
puts opt
end
end
opt_parser.parse!
How do I make these options get read? This is my guess (based off python experience). I'm just looking for it to print the lines right now:
if :config_file == true
File.open(:config_file, r) do |params|
puts params
end
end
if :host_file == true
File.open(:host_file, r) do |host|
put host
end
end
For taking arguments you need to use the following formats
"--switch=MANDATORY" or "--switch MANDATORY" # mandatory argument
"--switch[=OPTIONAL]" # optional argument
"--switch" # no argument
You are currently using the third format, which is being interpreted as taking no argument. You should use the first or second.
Also worth noting you probably want to be doing an assignment instead of a comparison when a flag is on
You want this
options[:host_file] = host_file
not this
options[:host_file] == host_file

How to generate OptionParser require arguments

The code below works, but I am manually raising argument errors for the required arguments using fetch, when I want to build the required arguments into the native OptionParser sytax for required parameters:
# ocra script.rb -- --type=value
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("--type [TYPE]",String, [:gl, :time], "Select Exception file type (gl, time)") do |t|
options["type"] = t
end
opts.on("--company [TYPE]",String, [:jaxon, :doric], "Select Company (jaxon, doric)") do |t|
options["company"] = t
end
end.parse!
opts = {}
opts['type'] = options.fetch('type') do
raise ArgumentError,"no 'type' option specified as a parameter (gl or time)"
end
opts['company'] = options.fetch('company') do
raise ArgumentError,"no 'company' option specified as a parameter (doric or jaxon)"
end
There's a similar question with an answer that may help you:
"How do you specify a required switch (not argument) with Ruby OptionParser?"
In short: there doesn't seem to be a way to make an option required (they are called options after all).
There is an OptionParser::MissingArgument exception that you could raise rather than the ArgumentError you're currently throwing.
Faced with the same situation, I ended up with an option like this. If not all of my mandatory options are provided, output the user-friendly help text generated by OptionParser based on my defined options. Feels cleaner than throwing an exception and printing a stack trace to the user.
options = {}
option_parser = OptionParser.new do |opts|
opts.banner = "Usage: #{$0} --data-dir DATA_DIR [options]"
# A non-mandatory option
opts.on('-p', '--port PORT', Integer, 'Override port number') do |v|
options[:port] = v
end
# My mandatory option
opts.on('-d', '--data-dir DATA_DIR', '[Mandatory] Specify the path to the data dir.') do |d|
options[:data_dir] = d
end
end
option_parser.parse!
if options[:data_dir].nil?
puts option_parser.help
exit 1
end

Ruby Options parser not reading command line options

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.

How do you specify a required switch (not argument) with Ruby OptionParser?

I'm writing a script and I want to require a --host switch with value, but if the --host switch isn't specified, I want the option parsing to fail.
I can't seem to figure out how to do that. The docs seem to only specify how to make the argument value mandatory, not the switch itself.
An approach using optparse that provides friendly output on missing switches:
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-f', '--from SENDER', 'username of sender') do |sender|
options[:from] = sender
end
opts.on('-t', '--to RECIPIENTS', 'comma separated list of recipients') do |recipients|
options[:to] = recipients
end
options[:number_of_files] = 1
opts.on('-n', '--num_files NUMBER', Integer, "number of files to send (default #{options[:number_of_files]})") do |number_of_files|
options[:number_of_files] = number_of_files
end
opts.on('-h', '--help', 'Display this screen') do
puts opts
exit
end
end
begin
optparse.parse!
mandatory = [:from, :to] # Enforce the presence of
missing = mandatory.select{ |param| options[param].nil? } # the -t and -f switches
unless missing.empty? #
raise OptionParser::MissingArgument.new(missing.join(', ')) #
end #
rescue OptionParser::InvalidOption, OptionParser::MissingArgument #
puts $!.to_s # Friendly output when parsing fails
puts optparse #
exit #
end #
puts "Performing task with options: #{options.inspect}"
Running without the -t or -f switches shows the following output:
Missing options: from, to
Usage: test_script [options]
-f, --from SENDER username of sender
-t, --to RECIPIENTS comma separated list of recipients
-n, --num_files NUMBER number of files to send (default 1)
-h, --help
Running the parse method in a begin/rescue clause allows friendly formatting upon other failures such as missing arguments or invalid switch values, for instance, try passing a string for the -n switch.
I am assuming you are using optparse here, although the same technique will work for other option parsing libraries.
The simplest method is probably to parse the parameters using your chosen option parsing library and then raise an OptionParser::MissingArgument Exception if the value of host is nil.
The following code illustrates
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--host HOSTNAME', "Mandatory Host Name") do |f|
options[:host] = f
end
end
optparse.parse!
#Now raise an exception if we have not found a host option
raise OptionParser::MissingArgument if options[:host].nil?
puts "Host = #{options[:host]}"
Running this example with a command line of
./program -h somehost
simple displays "Host = somehost"
Whilst running with a missing -h and no file name produces the following output
./program:15: missing argument: (OptionParser::MissingArgument)
And running with a command line of ./program -h produces
/usr/lib/ruby/1.8/optparse.rb:451:in `parse': missing argument: -h (OptionParser::MissingArgument)
from /usr/lib/ruby/1.8/optparse.rb:1288:in `parse_in_order'
from /usr/lib/ruby/1.8/optparse.rb:1247:in `catch'
from /usr/lib/ruby/1.8/optparse.rb:1247:in `parse_in_order'
from /usr/lib/ruby/1.8/optparse.rb:1241:in `order!'
from /usr/lib/ruby/1.8/optparse.rb:1332:in `permute!'
from /usr/lib/ruby/1.8/optparse.rb:1353:in `parse!'
from ./program:13
I turned this into a gem you can download and install from rubygems.org:
gem install pickled_optparse
And you can checkout the updated project source code on github:
http://github.com/PicklePumpers/pickled_optparse
-- Older post info --
This was really, really bugging me so I fixed it and kept the usage super DRY.
To make a switch required just add a :required symbol anywhere in the array of options like so:
opts.on("-f", "--foo [Bar]", String, :required, "Some required option") do |option|
#options[:foo] = option
end
Then at the end of your OptionParser block add one of these to print out the missing switches and the usage instructions:
if opts.missing_switches?
puts opts.missing_switches
puts opts
exit
end
And finally to make it all work you need to add the following "optparse_required_switches.rb" file to your project somewhere and require it when you do your command line parsing.
I wrote up a little article with an example on my blog:
http://picklepumpers.com/wordpress/?p=949
And here's the modified OptionParser file with an example of its usage:
required_switches_example.rb
#!/usr/bin/env ruby
require 'optparse'
require_relative 'optparse_required_switches'
# Configure options based on command line options
#options = {}
OptionParser.new do |opts|
opts.banner = "Usage: test [options] in_file[.srt] out_file[.srt]"
# Note that :required can be anywhere in the parameters
# Also note that OptionParser is bugged and will only check
# for required parameters on the last option, not my bug.
# required switch, required parameter
opts.on("-s Short", String, :required, "a required switch with just a short") do |operation|
#options[:operation] = operation
end
# required switch, optional parameter
opts.on(:required, "--long [Long]", String, "a required switch with just a long") do |operation|
#options[:operation] = operation
end
# required switch, required parameter
opts.on("-b", "--both ShortAndLong", String, "a required switch with short and long", :required) do |operation|
#options[:operation] = operation
end
# optional switch, optional parameter
opts.on("-o", "--optional [Whatever]", String, "an optional switch with short and long") do |operation|
#options[:operation] = operation
end
# Now we can see if there are any missing required
# switches so we can alert the user to what they
# missed and how to use the program properly.
if opts.missing_switches?
puts opts.missing_switches
puts opts
exit
end
end.parse!
optparse_required_switches.rb
# Add required switches to OptionParser
class OptionParser
# An array of messages describing the missing required switches
attr_reader :missing_switches
# Convenience method to test if we're missing any required switches
def missing_switches?
!#missing_switches.nil?
end
def make_switch(opts, block = nil)
short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
ldesc, sdesc, desc, arg = [], [], []
default_style = Switch::NoArgument
default_pattern = nil
klass = nil
n, q, a = nil
# Check for required switches
required = opts.delete(:required)
opts.each do |o|
# argument class
next if search(:atype, o) do |pat, c|
klass = notwice(o, klass, 'type')
if not_style and not_style != Switch::NoArgument
not_pattern, not_conv = pat, c
else
default_pattern, conv = pat, c
end
end
# directly specified pattern(any object possible to match)
if (!(String === o || Symbol === o)) and o.respond_to?(:match)
pattern = notwice(o, pattern, 'pattern')
if pattern.respond_to?(:convert)
conv = pattern.method(:convert).to_proc
else
conv = SPLAT_PROC
end
next
end
# anything others
case o
when Proc, Method
block = notwice(o, block, 'block')
when Array, Hash
case pattern
when CompletingHash
when nil
pattern = CompletingHash.new
conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
else
raise ArgumentError, "argument pattern given twice"
end
o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
when Module
raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
when *ArgumentStyle.keys
style = notwice(ArgumentStyle[o], style, 'style')
when /^--no-([^\[\]=\s]*)(.+)?/
q, a = $1, $2
o = notwice(a ? Object : TrueClass, klass, 'type')
not_pattern, not_conv = search(:atype, o) unless not_style
not_style = (not_style || default_style).guess(arg = a) if a
default_style = Switch::NoArgument
default_pattern, conv = search(:atype, FalseClass) unless default_pattern
ldesc << "--no-#{q}"
long << 'no-' + (q = q.downcase)
nolong << q
when /^--\[no-\]([^\[\]=\s]*)(.+)?/
q, a = $1, $2
o = notwice(a ? Object : TrueClass, klass, 'type')
if a
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
ldesc << "--[no-]#{q}"
long << (o = q.downcase)
not_pattern, not_conv = search(:atype, FalseClass) unless not_style
not_style = Switch::NoArgument
nolong << 'no-' + o
when /^--([^\[\]=\s]*)(.+)?/
q, a = $1, $2
if a
o = notwice(NilClass, klass, 'type')
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
ldesc << "--#{q}"
long << (o = q.downcase)
when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
q, a = $1, $2
o = notwice(Object, klass, 'type')
if a
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
sdesc << "-#{q}"
short << Regexp.new(q)
when /^-(.)(.+)?/
q, a = $1, $2
if a
o = notwice(NilClass, klass, 'type')
default_style = default_style.guess(arg = a)
default_pattern, conv = search(:atype, o) unless default_pattern
end
sdesc << "-#{q}"
short << q
when /^=/
style = notwice(default_style.guess(arg = o), style, 'style')
default_pattern, conv = search(:atype, Object) unless default_pattern
else
desc.push(o)
end
end
default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
if !(short.empty? and long.empty?)
s = (style || default_style).new(pattern || default_pattern, conv, sdesc, ldesc, arg, desc, block)
elsif !block
if style or pattern
raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
end
s = desc
else
short << pattern
s = (style || default_style).new(pattern, conv, nil, nil, arg, desc, block)
end
# Make sure required switches are given
if required && !(default_argv.include?("-#{short[0]}") || default_argv.include?("--#{long[0]}"))
#missing_switches ||= [] # Should be placed in initialize if incorporated into Ruby proper
# This is more clear but ugly and long.
#missing = "-#{short[0]}" if !short.empty?
#missing = "#{missing} or " if !short.empty? && !long.empty?
#missing = "#{missing}--#{long[0]}" if !long.empty?
# This is less clear and uglier but shorter.
missing = "#{"-#{short[0]}" if !short.empty?}#{" or " if !short.empty? && !long.empty?}#{"--#{long[0]}" if !long.empty?}"
#missing_switches << "Missing switch: #{missing}"
end
return s, short, long,
(not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
nolong
end
end
I came up with a clear and concise solution that sums up your contributions. It raises an OptionParser::MissingArgument exception with the missing arguments as a message. This exception is catched in the rescue block along with the rest of exceptions coming from OptionParser.
#!/usr/bin/env ruby
require 'optparse'
options = {}
optparse = OptionParser.new do |opts|
opts.on('-h', '--host hostname', "Host name") do |host|
options[:host] = host
end
end
begin
optparse.parse!
mandatory = [:host]
missing = mandatory.select{ |param| options[param].nil? }
raise OptionParser::MissingArgument, missing.join(', ') unless missing.empty?
rescue OptionParser::ParseError => e
puts e
puts optparse
exit
end
Running this example:
 ./program
missing argument: host
Usage: program [options]
-h, --host hostname Host name
If host is required, then surely it isn't an option, it's an argument.
With that in mind, here's a way to solve your problem. You can interrogate the ARGV array to see if a host has been specified, and, if it hasn't been, then call abort("You must specify a host!"), or similar, to make your program quit with an error status.
If you do something like this:
opts.on('-h', '--host',
'required host name [STRING]') do |h|
someoptions[:host] = h || nil
end
Then the someoptions[:host] will either be the value from the commandline or nil (if you don't supply --host and/or no value after --host) and you can test for it easily (and conditionally fail) after the parse:
fail "Hostname not provided" unless someoptions[:host]
The answer from unknown (google) is good, but contains a minor error.
rescue OptionParser::InvalidArgument, OptionParser::MissingArgument
should be
OptionParser::InvalidOption, OptionParser::MissingArgument
Otherwise, optparse.parse! will trigger the standard error output for OptionParser::InvalidOption, not the custom message.
The idea is to define an OptionParser, then parse! it, and puts it if some fields are missing. Setting filename to empty string by default is probably not the best way to go, but you got the idea.
require 'optparse'
filename = ''
options = OptionParser.new do |opts|
opts.banner = "Usage: swift-code-style.rb [options]"
opts.on("-iNAME", "--input-filename=NAME", "Input filename") do |name|
filename = name
end
opts.on("-h", "--help", "Prints this help") do
puts opts
exit
end
end
options.parse!
if filename == ''
puts "Missing filename.\n---\n"
puts options
exit
end
puts "Processing '#{filename}'..."
If -i filename is missing, it displays:
~/prj/gem/swift-code-kit ./swift-code-style.rb
Missing filename.
---
Usage: swift-code-style.rb [options]
-i, --input-filename=NAME Input filename
-h, --help Prints this help

Resources