Search code examples
rubyoptparse

Using Ruby optparse to process list of files


Is it possible to process a list of files using Ruby's OptionParser, or does ARGV have to be parsed separately?

I am trying to parse command line parameters for a Ruby script running in Linux. Currently my script is invoked using a style like: ./run.rb /dir/* and I parse ARGV to get the list of files that it operates on. However, now I have a couple of actions that I am switching between, so rather than edit the script I want to add a command line option. So the script would be invoked as ./run.rb -a action -i /dir/*.

When I was trying to get this working I ran into problems with only getting the first file that is in the directory. "Parsing command-line arguments as wildcards" explains that the reason is that the shell is expanding the list of files and that when OptionParser processes an Array it looks for comma separated values. The answer also says:

Because the * wildcard gets replaced with space delimited filenames you'll have to post-process ARGV after OptionParser has run against it, or programmatically glob the directory and build the list that way. ARGV has all the files except the one picked up in the -a option so, personally, I'd drop the -a option and let ARGV contain all the files.

Is this correct that OptionParser doesn't have a way to processes space separated values as would be returned by shell expansion? I haven't found anything so I'm think my interpretation is correct. But I also think this would be a commonly used feature so I'm surprised that this is the case, hence the reason for this question.


Solution

  • Consider your script, but invoked with the options the other way round:

    ./run.rb -i /dir/* -a action
    

    Assuming that /dir/ contains two files, foo and bar, this will be expanded by the shell to:

    ./run.rb -i /dir/bar /dir/foo -a action
    

    How would the script distinguish between the following?

    1. two files being specified as the value of the -i option and the -a option being specified with action as the value (the intended case), and

    2. four files being specified as the values of the -i action (/dir/bar and /dir/foo as before, plus -a and action)

    The only way to do this is to insist the -i option is always last so there are no other options left, in which case you may as well do away with that option altogether and do what the answer you quote suggests.

    Bear in mind that the shell does wildcard (and other) expansion and then splits the command on whitespace to create an array of strings, and does this before your script gets to see it, so this question is more to do with the shell than OptionParser (the same issue would affect any option parsing library in any language). I guess passing a list of files as an option to a command (rather than the main input) hasn’t come up enough to warrant changing shell behaviour or adding a feature to expand in a different way (at least I’m not aware of any such alternative expansion).