views:

59

answers:

6

Hey all -

I'm working on a C++ program that accepts a filename as a command line input, reads a bunch of data from that file, computes two things about the data, then exits. One of those computations must be done, but the other is much more optional, so I thought it would be neat to add a -c flag to turn it off. Naively, I would just always assume that if the user gives two arguments, the first must toggle the computation and the second gives the filename, but this gets much more cumbersome with more flags. What is the standard practice for making rm -rf "file", rm -fr "file", and rm "file" -rf all work as valid commands?

A: 

A Google search turns up this C++ class, called "Anyoption". It looks like it is exactly what you need. http://www.hackorama.com/anyoption/

You can always write your own parser, as well. I expect that, if nothing else, writing your own will be an excellent programming exercise, as it's not too difficult, but is not trivial.

ChessWhiz
+1  A: 

The standard argument-parsing function used by UNIX utilities, such as rm, is getopt(). Newer commands might use getopt_long(), which handles so-called "long arguments" of more than one character (long --symbols versus short -s).

For an example of its use, take a look at this implementation of rm, or just check the man page.

getopt() has been ported to Win32/MFC. One such port is XGetOpt.

Jeremy W. Sherman
A: 

Or, if you have boost, program_options does the trick... http://www.boost.org/doc/libs/1_44_0/doc/html/program_options.html

Nim
+2  A: 

Use boost program options . Example:

po::options_description desc("Allowed options");
desc.add_options()
    ("help", "produce help message")
    ("compression", po::value<int>(), "set compression level")
;

po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
po::notify(vm);    

if (vm.count("help")) {
    cout << desc << "\n";
    return 1;
}

if (vm.count("compression")) {
    cout << "Compression level was set to " 
 << vm["compression"].as<int>() << ".\n";
} else {
    cout << "Compression level was not set.\n";
}
Armen Tsirunyan
+3  A: 

What is the standard practice for making rm -rf "file", rm -fr "file", and rm "file" -rf all work as valid commands?

A lot of people disagree on that, so there seems to be no standard practice, unless you're targetting a specific audience (i.e. when writing a linux-specific command-line tool, check out what "standard" utilities do).

In any case, most libraries end up given you the following options:

  1. Switches: these isolated flags in that no argument is expected after the switch. Unix tools such as tar and rm often allow you to specify multiple flags together just as in your example's -rf flags.
  2. Options: these are specified like switches but expect some value after the switch. gcc, for instance, expects the output file's name after -o. Because they expect options, they may obviously not be specified simultaneously.
  3. Short VS long names: switches and options typically have two names a single dash, single letter or double-dash descriptive name (compare -o and --output-file) although some options, such as help or verbosity control, often have only a long name.
  4. Multiple trailing arguments: if the tool allows to process simultaneous files at once, these are all specified at the end of the sequence only. Allowing these to be interleaved with other options and flags would lead to a nightmare. A -- option to indicate "end of options" is also convenient in case some of those trailing arguments may contain dashes.

A lot of tools allow switches and options to be interleaved, but multiple trailing arguments are always at the end.

Of course, you will find commonly used tools that don't respect these conventions, such as Microsoft's cl.exe and link.exe. More recent tools seem to converge towards these though, including Microsoft's candle.exeand light.exe for WIX.

If you want to make sure you respect these guidelines (and save a lot of time), use a "standard" library, such as UNIX's getopt() or boost's program options.

Edit: existing libraries often generate the --help option for you using the short descriptions you give when you specify the switches and options you are expecting.

André Caron
A: 

The desired behavior is demonstrated by the modern GNU tools that you will find in a current Linux distribution.

The old nasty behavior can be seen in the BSD tools that are provided by default on FreeBSD and OpenBSD.

The desired behavior is:

  • able to combine all the short switches without arguments together in one place
  • also able to provide short options seperately.
  • able to mix options and arguments at will, providing options before and after filename arguments.
  • every option should also have a long, verbose option specified with two dashes.
  • two dashes by themselves should terminate option processing: this enables handling filenames that look like options.

With the GNU versions of ls you can do this:
ls -al *.c -h *.h --reverse -t -- -filename-1 -filename-2

This is very nice for command line users. It should be. We wrote it.

The BSD version of ls will choke on that. It is very annoying to be forced to navigate back to the beginning of the command just to change some display options.

Zan Lynx