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

前端 未结 8 2066
时光取名叫无心
时光取名叫无心 2020-12-12 23:58

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 fai

相关标签:
8条回答
  • 2020-12-13 00:22

    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.

    0 讨论(0)
  • 2020-12-13 00:26

    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]
    
    0 讨论(0)
  • 2020-12-13 00:26

    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
    
    0 讨论(0)
  • 2020-12-13 00:33

    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
    
    0 讨论(0)
  • 2020-12-13 00:36

    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.

    0 讨论(0)
  • 2020-12-13 00:36

    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.

    0 讨论(0)
提交回复
热议问题