- A STORY
"--name <SPC> value" style options considered harmful for end-user scripting.
This document describes my opinion about design of CLI option handling for end-user scripting area. My opinion is: let's advertise
"--name <SPC> value" and make later unofficial, especially when you are at field of end-user scripting.
Original version of this document (in Japanese) can be found at here. 日本語版もあるよ！
Some months ago, you wrote a CLI program, called A. It has 5 CLI options for now, and is likely to have more options next few months. To describe its typical usage, you wrote its manual and put a synopsis like following:
% A --debug --port 5050 --dbname master.db --log var/log/foo.log --type XYZ
Your colleagues use this program A almost everyday. Although they are employed as specialists other than about programming, some of them can program too (not so good at it than you;-). And importantly, they too love automating boring tasks like you does.
Eventually, one of your colleague wrote another program B (which might be a daily batch or a wrapper just to give typical options by default) start calling your program A.
% B # => calls: A --port 5050 --dbname master.db --log var/log/foo.log --type ABC
B delegates most of options to A as-is.
% B --dbname devel.db --port 8080 # => calls: A --port 8080 --dbname devel.db --log var/log/foo.log --type ABC
But of course as usual, B itself has own options too.
% B --company ZZZ # => calls: A --port 5050 --dbname master_ZZZ.db --log var/log/foo.log --type ABC-ZZZ
In the synopsis of B in its manual, the author of B wrote its additional option as
--company=ZZZlike above. This is because you implicitly taught him/her to do so through your manual of A.
Since your colleagues respect your programming experiences, they have strong motive to copy your coding styles/CLI designs. As you wrote options of A as
--name value(space separated form) than
--name=value(equal concatenated form) in the manual, your colleagues are very likely to start thinking former
--name valueis better than later and mimic it in their programs and manuals.
If your program A takes space separated form options
--name value(which means value is given as another argument), it must have complete dictionary of option names anyway to parse CLI arguments correctly. Because there is no way than the dictionary to determine next coming arguments are the value of preceding option.
Note: This dictionary usually appears implicitly as arguments of Getopt::Long::GetOptions() like following:
use Getopt::Long; ... GetOptions( "debug" => \ my $o_debug, "port=i" => \ my $o_port, "dbname=s" => \ my $o_dbname, "log=s" => \ my $o_logfile, "type=s" => \ my $o_type, ) or die("Error in command line arguments\n");
Same applies to program B by your colleague. It must have same entries in the dictionary and may have own options too.
use Getopt::Long; ... GetOptions( "debug" => \ my $o_debug, "port=i" => \ my $o_port, "dbname=s" => \ my $o_dbname, "log=s" => \ my $o_logfile, "type=s" => \ my $o_type, "company=s"=> \ my $o_company, # New option ) or die("Error in command line arguments\n");
Now it is very easy to imagine another colleague starts writing yet another program C, which calls B, which calls A.
% C --port 18000 # => calls: B --company ZZZ --port 18000 # => calls: A --port 18000 --dbname master_ZZZ.db --log var/log/foo.log --type ABC-ZZZ
To achieve option delegation of
--portlike above C must know the options dictionary of B. And then another program D arrives, followed by E... you can't prohibit others to write a wrapper for your program, can you?
Biggest annoyance comes when you add new option to A, the starting point of the chain. The authors of B, C, D... must update the option dictionaries too, usually by-hand. Otherwise, they can't get benefit from your new feature in A.
This is very similar to fire fighting with bucket relay which IMHO is very inefficient and only acceptable when there is no better ways.
How can we end this chain of maintainances by-hand? Probably, there could be a systematic approach to maintain these chains of option specifications. But I don't know such implementation widely accepted in Perl and even if there is such thing, IMHO, it is too much for casual automation by end-users. Especially if you want to encourage them to write more scripts.
So instead, I want a Poorman's approach
(Or some kind of Zen)
To remove chains of maintainances completely
Which should be written easily and correctly even by novice end-users.
Could we have such a way?
Forget about existing inferior programs written by others. They are uncontrollable. Instead focus on programs you and your collegues are going to write for your team. Establish some kind of descipline/standard about option specification to enable dictionary-less option delegation. For example, officially recommend only following 3 styles of options:
Equal-concatenated pair of option name and its value.
Option name without a value.
Single letter option(without a value).
-- as an end of option parsing as usual.
This way, all options are regularized to start with
- regardless of having a value or not. So any arguments starting with
- can be gathered and passed through another program without predefined dictionary. Unlike dictionary based approach, this option processing is write once and use forever. If programs like B, C..., written by your colleagues, follow this descipline too, they can delegate all forthcoming options to program A. Of course other problems like option name collision can still remain, but it is smaller than original one.
Folks, I strongly fond of automation. Yes, I actually love it. If traditional style of option handling can curse future programs written by my colleagues, I can't advocate it anymore. This is why I don't implement
"--name <SPC> value" style options in my important products.
Thank you for reading!