Ruby ARGV array search - ruby

I am having some issues searching for what options have been passed in the ARGV. I have,
if ARGV.include? '-v' == true
puts "Do junk"
else
puts "Nope"
end
This seems like a shockingly simple thing but I always get "Nope" either when I place "-v" or when I do not. Am I missing something simple here?

I don know if it's your problem, but you don't need the == true.
if ARGV.include? '-v'
puts "Do junk"
else
puts "Nope"
end
The include? method returns true or false.

Why are you searching ARGV for options? Use the standard library's OptionParser class, which is made for this.
require 'optparse'
require 'pp'
options = {}
OptionParser.new { |opt|
opt.on('-v', '--verbose', 'Be verbose') { |o| options[:verbose] = o }
}.parse!
pp options
Save that, and run it with something like: ruby test.rb -v or ruby test.rb --verbose and you'll see:
{:verbose=>true}
Run it with ruby test.rb -h or ruby test.rb --help and you'll see:
Usage: test [options]
-v, --verbose Be verbose
OptionParser has all sorts of nice (and intelligent) tricks for setting booleans, required parameters, coercing values so you can get multiple values for a parameter returned as an array, etc. And, notice that it created the help for me, including using -h or --help for the flag.
Look at the examples in the documentation for additional ideas of what it can do.

This is a precedence problem. Write as'
if ARGV.include?('-v') == true
puts "Do junk"
else
puts "Nope"
end
In your case if ARGV.include? '-v' == true has been interpreted as if ARGV.include?
('-v' == true). Now '-v' == true returns false, and false is not included in your ARGV array, so if evaluates as false, and else part is getting executed.
Better to write as
if ARGV.include? '-v'
puts "Do junk"
else
puts "Nope"
end
Because ARGV is an array, and Array#include?(ob) returns true, if the ob found inside the array.

All of the other answers so far are right in that it's a precedence problem.
Here are a couple helpful notes, the first of which has already been touched on:
You don't need == true, since the include? method already returns true or false.
For parsing command-line arguments, you might find it more intuitive to use a library like Trollop. Using Trollop, your example could be done like this:
require 'trollop'
opts = Trollop::options do
opt :verbose, "Run verbosely" # (-v is created automatically from this)
end
if opts[:v]
puts "yes"
else
puts "no"
end
(EDIT: See also the Tin Man's answer re: optparse)

Related

When is the do keyword required in Ruby?

For example, does the presence or absence of do in the following code affect the behavior of the program at all?
while true do
puts "Hi"
break
end
while true
puts "Hi"
break
end
According to The Ruby Programming Language book Section 5.2.1:
The do keyword in a while or until loop is like the then keyword in an
if statement: it may be omitted altogether as long as a newline (or
semicolon) appears between the loop condition and the loop body.
So, no, it won't change the behavior, it's just optional syntax.
Let's find out!
For a quick answer we can look at Ruby's documentation and find http://www.ruby-doc.org/core-2.1.1/doc/syntax/control_expressions_rdoc.html#label-while+Loop which states that
The do keyword is optional.
Ok so these two examples are equivalent but are they identical? They might do the same thing but maybe there's a reason to favor one over the other. We can look at the AST these examples generate and see if there's any difference.
> gem install ruby_parser
> irb
> require 'ruby_parser'
=> true
> with_do = <<-END
while true do
puts "Hi"
break
end
END
=> "while true do\n puts \"Hi\"\n break\nend\n"
> without_do = <<-END
while true
puts "Hi"
break
end
END
=> "while true\n puts \"Hi\"\n break\nend\n"
> RubyParser.new.parse with_do
=> s(:while, s(:true), s(:block, s(:call, nil, :puts, s(:str, "Hi")), s(:break)), true)
> RubyParser.new.parse without_do
=> s(:while, s(:true), s(:block, s(:call, nil, :puts, s(:str, "Hi")), s(:break)), true)
Nope. These two examples execute the exact same instructions so we can pick whichever style we find easier to read. A common preference is to omit the do when possible: https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do

Do something if no arguments present

This is a small test script I wrote:
#!/usr/bin/env ruby
require 'packetfu'
def mac(host)
if host
rmac = PacketFu::Utils.arp(host, :iface => 'wlan0')
puts rmac
else
whoami = PacketFu::Utils.whoami?(:iface => 'wlan0')
puts whoami
end
end
mac(ARGV[0])
What I want to do is have it print the second variable if no argument is specified. Instead I get an ArgumentError. There's obviously an easier way that I'm just missing.
If you want to be able to call the function without any arguments, you need to change its definition to such that it does not require an argument. One way is to give the argument a default value. Then you can check for that, e.g.,
def mac(host = nil)
if host
puts "host: #{host}"
else
puts 'no host'
end
end
If you want to distinguish between no argument given and argument given with the default value, you could use a variable number of arguments:
def mac2(*args)
if args.empty?
puts "no arguments"
else
host = args[0]
end
end
On the other hand, if your problem is detecting whether ARGV was empty (i.e., no command-line argument given), you can check that higher up. For example, only call mac if an argument was given:
if ARGV.empty?
puts "Usage: …"
exit 1
end
mac(ARGV[0])

What's the most elegant way to check for the existence of a command line tool on the system from Ruby?

For reference, the Bash equivalents are discussed here: Check if a program exists from a Bash script
I've been doing something like
if `which commandname` =~ /commandname/
# do stuff
end
but I'm wondering if there is something a little cleaner.
system("ls")
#=> true
system("ls wrong params")
#=> false
system("lss")
#=> nil
if system("your cmd")
puts "yey!"
else
puts "oups"
end

Ruby IF no ARGV given

This is my code:
if ARGV[0] == false
puts "Usage: ./script.rb argument"
exit
end
print "Yey we got an argument: " ARGV[0]
But I just cant make the code check if ARGV[0] is given or not, how should I do that ?
Check if it's empty? (or check its length):
if ARGV.empty?
puts ...
exit
end
Consider using any of the Ruby command line argument parsers, like OptionParser.
The simplest way to process positional arguments (other than using a gem to do it) is to shift them off, one at a time:
arg1 = ARGV.shift
arg2 = ARGV.shift
A missing argument will be nil. Let's exploit that to give arg2 a default value:
arg1 = ARGV.shift
arg2 = ARGV.shift || 'default value goes here'
Checking for a required argument is trivial:
raise "missing argument" unless arg1
It's also easy to see if too many arguments have been supplied:
raise "too many arguments" unless ARGV.empty?
I realize a good solution is already given, but nobody mentioned the real reason why your example didn't work.
The issue with your example code is that if there are no arguments given, ARGV[0] will return nil, and nil == false is false. nil is "falsy", but not equal to false. What you could have done is:
unless ARGV[0]
puts "Usage: ..."
exit 1
end
Which would have worked, because this statement now depends on falsyness, rather than on equality to the actual false.
But don't use this code, it's far more clear if you state your actual intent, which is that you want to know if there are any arguments (instead of whether the first argument is falsy). So use the code others suggested:
if ARGV.empty?
puts "Usage..."
exit 1
end
To check if ARGV[0] (or more args) is given or not:
puts 'Usage: ...' if ARGV.length == 0
You could also use ARGV.empty? (#DaveNewton's answer)
You can check the length of ARGV. If it has zero elements then ARGV[0] wasn't given.
I do this and work's:
url = ARGV[0]
if url.nil?
puts "The shell script needs the arguments."
exit
end
Here's what I do:
a = 'defaultvalue' if (a = ARGV.shift).nil?
Someone else suggested this:
a = ARGV.shift || 'defaultvalue'
This is wrong, since any false value will cause defaultvalue to be assigned, e.g.:
args=[false]; a=args.shift||true; # a has final value true when it should be false

Really Cheap Command-Line Option Parsing in Ruby

EDIT: Please, please, please read the two requirements listed at the bottom of this post before replying. People keep posting their new gems and libraries and whatnot, which clearly don't meet the requirements.
Sometimes I want to very cheaply hack some command line options into a simple script. A fun way to do it, without dealing with getopts or parsing or anything like that, is:
...
$quiet = ARGV.delete('-d')
$interactive = ARGV.delete('-i')
...
# Deal with ARGV as usual here, maybe using ARGF or whatever.
It's not quite the normal Unix options syntax, because it will accept options non-option command line parameters, as in "myprog -i foo bar -q", but I can live with that. (Some people, such as the Subversion developers, prefer this. Sometimes I do too.)
An option that's just present or absent can't be implemented much more simply than the above. (One assignment, one function call, one side effect.) Is there an equally simple way to deal with options that take a parameter, such as "-f filename"?
EDIT:
One point I didn't make earlier on, because it hadn't become clear to me until the author of Trollop mentioned that the library fit "in one [800-line] file," is that I'm looking not only for clean syntax, but for a technique that has the following characteristics:
The entirety of the code can be included in the script file (without overwhelming the actual script itself, which may be only a couple of dozen lines), so that one can drop a single file in a bin dir on any system with a standard Ruby 1.8.[5-7] installation and use it. If you can't write a Ruby script that has no require statements and where the code to parse a couple of options is under a dozen lines or so, you fail this requirement.
The code is small and simple enough that one can remember enough of it to directly type in code that will do the trick, rather than cutting and pasting from somewhere else. Think of the situation where you're on the console of a firewalled sever with no Internet access, and you want to toss together a quick script for a client to use. I don't know about you, but (besides failing the requirement above) memorizing even the 45 lines of simplified micro-optparse is not something I care to do.
As the author of Trollop, I cannot BELIEVE the stuff that people think is reasonable in an option parser. Seriously. It boggles the mind.
Why should I have to make a module that extends some other module to parse options? Why should I have to subclass anything? Why should I have to subscribe to some "framework" just to parse the command line?
Here's the Trollop version of the above:
opts = Trollop::options do
opt :quiet, "Use minimal output", :short => 'q'
opt :interactive, "Be interactive"
opt :filename, "File to process", :type => String
end
And that's it. opts is now a hash with keys :quiet, :interactive, and :filename. You can do whatever you want with it. And you get a beautiful help page, formatted to fit your screen width, automatic short argument names, type checking... everything you need.
It's one file, so you can drop it in your lib/ directory if you don't want a formal dependency. It has a minimal DSL that is easy to pick up.
LOC per option people. It matters.
I share your distaste for require 'getopts', mainly due to the awesomeness that is OptionParser:
% cat temp.rb
require 'optparse'
OptionParser.new do |o|
o.on('-d') { |b| $quiet = b }
o.on('-i') { |b| $interactive = b }
o.on('-f FILENAME') { |filename| $filename = filename }
o.on('-h') { puts o; exit }
o.parse!
end
p :quiet => $quiet, :interactive => $interactive, :filename => $filename
% ruby temp.rb
{:interactive=>nil, :filename=>nil, :quiet=>nil}
% ruby temp.rb -h
Usage: temp [options]
-d
-i
-f FILENAME
-h
% ruby temp.rb -d
{:interactive=>nil, :filename=>nil, :quiet=>true}
% ruby temp.rb -i
{:interactive=>true, :filename=>nil, :quiet=>nil}
% ruby temp.rb -di
{:interactive=>true, :filename=>nil, :quiet=>true}
% ruby temp.rb -dif apelad
{:interactive=>true, :filename=>"apelad", :quiet=>true}
% ruby temp.rb -f apelad -i
{:interactive=>true, :filename=>"apelad", :quiet=>nil}
Here's the standard technique I usually use:
#!/usr/bin/env ruby
def usage(s)
$stderr.puts(s)
$stderr.puts("Usage: #{File.basename($0)}: [-l <logfile] [-q] file ...")
exit(2)
end
$quiet = false
$logfile = nil
loop { case ARGV[0]
when '-q' then ARGV.shift; $quiet = true
when '-l' then ARGV.shift; $logfile = ARGV.shift
when /^-/ then usage("Unknown option: #{ARGV[0].inspect}")
else break
end; }
# Program carries on here.
puts("quiet: #{$quiet} logfile: #{$logfile.inspect} args: #{ARGV.inspect}")
Since nobody appeared to mention it, and the title does refer to cheap command-line parsing, why not just let the Ruby interpreter do the work for you? If you pass the -s switch (in your shebang, for example), you get dirt-simple switches for free, assigned to single-letter global variables. Here's your example using that switch:
#!/usr/bin/env ruby -s
puts "#$0: Quiet=#$q Interactive=#$i, ARGV=#{ARGV.inspect}"
And here's the output when I save that as ./test and chmod it +x:
$ ./test
./test: Quiet= Interactive=, ARGV=[]
$ ./test -q foo
./test: Quiet=true Interactive=, ARGV=["foo"]
$ ./test -q -i foo bar baz
./test: Quiet=true Interactive=true, ARGV=["foo", "bar", "baz"]
$ ./test -q=very foo
./test: Quiet=very Interactive=, ARGV=["foo"]
See ruby -h for details.
That must be as cheap as it gets. It will raise a NameError if you try a switch like -:, so there's some validation there. Of course, you can't have any switches after a non-switch argument, but if you need something fancy, you really should be using at the minimum OptionParser. In fact, the only thing that annoys me about this technique is that you'll get a warning (if you've enabled them) when accessing an unset global variable, but it's still falsey, so it works just fine for throwaway tools and quick scripts.
One caveat pointed out by FelipeC in the comments in "How to do really cheap command-line option parsing in Ruby", is that your shell might not support the 3-token shebang; you may need to replace /usr/bin/env ruby -w with the actual path to your ruby (like /usr/local/bin/ruby -w), or run it from a wrapper script, or something.
I built micro-optparse to fill this obvious need for a short, but easy to use option-parser. It has a syntax similar to Trollop and is 70 lines short. If you don't need validations and can do without empty lines you can cut it down to 45 lines. I think that's exactly what you were looking for.
Short example:
options = Parser.new do |p|
p.version = "fancy script version 1.0"
p.option :verbose, "turn on verbose mode"
p.option :number_of_chairs, "defines how many chairs are in the classroom", :default => 1
p.option :room_number, "select room number", :default => 2, :value_in_set => [1,2,3,4]
end.process!
Calling the script with -h or --help will print
Usage: micro-optparse-example [options]
-v, --[no-]verbose turn on verbose mode
-n, --number-of-chairs 1 defines how many chairs are in the classroom
-r, --room-number 2 select room number
-h, --help Show this message
-V, --version Print version
It checks if input is of same type as the default value, generates short and long accessors, prints descriptive error messages if invalid arguments are given and more.
I compared several option-parser by using each option-parser for the problem I had.
You can use these examples and my summary to make an informative decision.
Feel free to add more implementations to the list. :)
I totally understand why you want to avoid optparse - it can get too much. But there are a few far "lighter" solutions (compared to OptParse) that come as libraries but are simple enough to make a single gem installation worthwhile.
For example, check out this OptiFlag example. Just a few lines for the processing. A slightly truncated example tailored to your case:
require 'optiflag'
module Whatever extend OptiFlagSet
flag "f"
and_process!
end
ARGV.flags.f # => .. whatever ..
There are tons of customized examples too. I recall using another that was even easier, but it has escaped me for now but I will come back and add a comment here if I find it.
You can try something like:
if( ARGV.include( '-f' ) )
file = ARGV[ARGV.indexof( '-f' ) + 1 )]
ARGV.delete('-f')
ARGV.delete(file)
end
This is what I use for really, really cheap args:
def main
ARGV.each { |a| eval a }
end
main
so if you run programname foo bar it calls foo and then bar. It's handy for throwaway scripts.
Have you considered Thor by wycats? I think it's a lot cleaner than optparse. If you already have a script written, it might be some more work to format it or refactor it for thor, but it does make handling options very simple.
Here's the example snippet from the README:
class MyApp < Thor # [1]
map "-L" => :list # [2]
desc "install APP_NAME", "install one of the available apps" # [3]
method_options :force => :boolean, :alias => :optional # [4]
def install(name)
user_alias = options[:alias]
if options.force?
# do something
end
# ... other code ...
end
desc "list [SEARCH]", "list all of the available apps, limited by SEARCH"
def list(search = "")
# list everything
end
end
Thor automatically maps commands as such:
app install myname --force
That gets converted to:
MyApp.new.install("myname")
# with {'force' => true} as options hash
Inherit from Thor to turn a class into an option mapper
Map additional non-valid identifiers to specific methods. In this case, convert -L to :list
Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description.
Provide any additional options. These will be marshaled from -- and - params. In this case, a --force and a -f option is added.
Here's my favorite quick-and-dirty option parser:
case ARGV.join
when /-h/
puts "help message"
exit
when /-opt1/
puts "running opt1"
end
The options are regular expressions, so "-h" also would match "--help".
Readable, easy to remember, no external library, and minimal code.
Trollop is pretty cheap.
If you want a simple command line parser for key/value commands without the use of gems:
But this only works if you always have key/value pairs.
# example
# script.rb -u username -p mypass
# check if there are even set of params given
if ARGV.count.odd?
puts 'invalid number of arguments'
exit 1
end
# holds key/value pair of cl params {key1 => value1, key2 => valye2, ...}
opts = {}
(ARGV.count/2).times do |i|
k,v = ARGV.shift(2)
opts[k] = v # create k/v pair
end
# set defaults if no params are given
opts['-u'] ||= 'root'
# example use of opts
puts "username:#{opts['-u']} password:#{opts['-p']}"
If you don't' need any checking you could just use:
opts = {}
(ARGV.count/2).times do |i|
k,v = ARGV.shift(2)
opts[k] = v # create k/v pair
end
Here's the code snippet I use at the top of most of my scripts:
arghash = Hash.new.tap { |h| # Parse ARGV into a hash
i = -1
ARGV.map{ |s| /(-[a-zA-Z_-])?([^=]+)?(=)?(.+)?/m.match(s).to_a }
.each{ |(_,a,b,c,d)| h[ a ? "#{a}#{b}#{c}" : (i+=1) ] =
(a ? (c ? "#{d}" : true) : "#{b}#{c}#{d}")
}
[[:argc,Proc.new {|| h.count{|(k,_)| !k.is_a?(String)}}],
[:switches, Proc.new {|| h.keys.select{|k| k[0] == '-' }}]
].each{|(n,p)| h.define_singleton_method(n,&p) }
}
I also hate to require additional files in my quick-and-dirty scripts. My solution is very nearly what you're asking for. I paste a 10 line snippet of code at the top of any of my scripts that parses the command line and sticks positional args and switches into a Hash object (usually assigned to a object that I've named arghash in the examples below).
Here's an example command line you might want to parse...
./myexampleprog.rb -s -x=15 --longswitch arg1 --longswitch2=val1 arg2
Which would become a Hash like this.
{
'-s' => true,
'-x=' => '15',
'--longswitch' => true,
'--longswitch2=' => 'val1',
0 => 'arg1',
1 => 'arg2'
}
In addition to that, two convenience methods are added to the Hash:
argc() will return the count of non-switch arguments.
switches() will return an array containing the keys for switches that are present
This is mean to allow some quick and dirty stuff like...
Validate I've got the right number of positional arguments regardless of the switches passed in ( arghash.argc == 2 )
Access positional arguments by their relative position, regardless of switches appearing before or interspersed with positional arguments ( e.g. arghash[1] always gets the second non-switch argument).
Support value-assigned switches in the command line such as "--max=15" which can be accessed by arghash['--max='] which yields a value of '15' given the example command line.
Test for the presence or absence of a switch in the command line using a very simple notation such as arghash['-s'] which evaluates to true if it's present and nil if its absent.
Test for the presence of a switch or alternatives of switches using set operations like
puts USAGETEXT if !(%w(-h --help) & arghash.switches()).empty?
Identify use of invalid switches using set operations such as
puts "Invalid switch found!" if !(arghash.switches - %w(-valid1 -valid2)).empty?
Specify default values for missing arguments using a simple Hash.merge() such as the below example that fills in a value for -max= if one was not set and adds a 4th positional argument if one was not passed.
with_defaults = {'-max=' => 20, 3 => 'default.txt'}.merge(arghash)
This is very similar to the accepted answer, but using ARGV.delete_if which is what I use in my simple parser. The only real difference is that options with arguments must be together (e.g. -lfile).
def usage
"usage: #{File.basename($0)}: [-l<logfile>] [-q] file ..."
end
ARGV.delete_if do |cur|
next false if cur[0] != '-'
case cur
when '-q'
$quiet = true
when /^-l(.+)$/
$logfile = $1
else
$stderr.puts "Unknown option: #{cur}"
$stderr.puts usage
exit 1
end
end
Apparently #WilliamMorgan and I think alike. I just released last night to Github what I now see is a similar library to Trollop (Named how?) after having done a search for OptionParser on Github, see Switches
There are a few differences, but the philosophy is the same. One obvious difference is that Switches is dependent on OptionParser.
I'm developing my own option parser gem called Acclaim.
I wrote it because I wanted to create git-style command line interfaces and be able to cleanly separate the functionality of each command into separate classes, but it can also be used without the entire command framework as well:
(options = []) << Acclaim::Option.new(:verbose, '-v', '--verbose')
values = Acclaim::Option::Parser.new(ARGV, options).parse!
puts 'Verbose.' if values.verbose?
No stable release as of yet, but I've already implemented some features like:
custom option parser
flexible parsing of option's arguments that allows for both minimum and optional
support for many option styles
replace, append or raise on multiple instances of the same option
custom option handlers
custom type handlers
predefined handlers for the common standard library classes
There's a lot of emphasis on commands so it might be a little heavy for simple command line parsing but it works well and I've been using it on all of my projects. If you're interested in the command interface aspect then check out the project's GitHub page for more information and examples.
Suppose a command has at most one action and arbitrary number of options like this:
cmd.rb
cmd.rb action
cmd.rb action -a -b ...
cmd.rb action -ab ...
The parsing without validation may be like this:
ACTION = ARGV.shift
OPTIONS = ARGV.join.tr('-', '')
if ACTION == '***'
...
if OPTIONS.include? '*'
...
end
...
end
https://github.com/soveran/clap
other_args = Clap.run ARGV,
"-s" => lambda { |s| switch = s },
"-o" => lambda { other = true }
46LOC (at 1.0.0), no dependency on external option parser. Gets the job done. Probably not as full featured as others, but it's 46LOC.
If you check the code you can pretty easily duplicate the underlying technique -- assign lambdas and use the arity to ensure the proper number of args follow the flag if you really don't want an external library.
Simple. Cheap.
EDIT: the underlying concept boiled down as I suppose you might copy/paste it into a script to make a reasonable command line parser. It's definitely not something I would commit to memory, but using the lambda arity as a cheap parser is a novel idea:
flag = false
option = nil
opts = {
"--flag" => ->() { flag = true },
"--option" => ->(v) { option = v }
}
argv = ARGV
args = []
while argv.any?
item = argv.shift
flag = opts[item]
if flag
raise ArgumentError if argv.size < arity
flag.call(*argv.shift(arity))
else
args << item
end
end
# ...do stuff...
I created a very simple yet useful parser: parseopt. It uses Git's internal option parser as inspiration, and also Ruby's OptionParser.
It looks like this:
opts = ParseOpt.new
opts.usage = 'git foo'
opts.on('b', 'bool', 'Boolean') do |v|
$bool = v
end
opts.on('s', 'string', 'String') do |v|
$str = v
end
opts.on('n', 'number', 'Number') do |v|
$num = v.to_i
end
opts.parse

Resources