💾 Archived View for thrig.me › tech › flags.gmi captured on 2023-09-08 at 16:56:33. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-04-19)
-=-=-=-=-=-=-
These also appear as command options, switches, arguments, and maybe other terms as well. Sometimes they are documented in hard to remember locations, such as perlrun(1). Usually they customize the behavior of a program, app, or script in some often optional way.
There are, as always, pitfalls.
Let us consider the Perl Getopt::Long module, a common choice. Suppose we have an app (script, or program) that in version 1 supports a source flag
#!/usr/bin/env perl # opt1 - first example program version use 5.10.0; use Getopt::Long qw(GetOptions); GetOptions( 'source=s' => \my $Flag_Source ); say $Flag_Source ? "Src: $Flag_Source" : "nope";
and in version 2 also a simulation flag
#!/usr/bin/env perl # opt2 - now with more features use 5.16.0; use warnings; use Getopt::Long qw(GetOptions); GetOptions( 'source=s' => \my $Flag_Source, 'simulation=i' => \my $Flag_Simnum ) or exit 1; say "Src: ", $Flag_Source ? $Flag_Source : 'nope'; say "Sim: ", $Flag_Simnum ? $Flag_Simnum : '-1';
and some time later, perhaps after the bug reports have come in, a version 3, thus
#!/usr/bin/env perl # opt3 - and now with bugfixes use 5.16.0; use warnings; use Getopt::Long qw(GetOptions); GetOptions( 'simulation=i' => \my $Flag_Simnum, 'source|s=s' => \my $Flag_Source, 'version|V' => sub { warn "3\n"; exit 1 }, ) or exit 1; say "Sim: ", $Flag_Simnum ? $Flag_Simnum : '-1'; say "Src: ", $Flag_Source ? $Flag_Source : 'nope';
What is the problem here, if you have not already spotted it?
$ perl opt1 nope $ perl opt1 -s foo Src: foo $ perl opt2 -s foo Option s is ambiguous (simulation, source)
The first two versions of our program have conflicting options; the first supports -s but the second does not. Worse, this may not be noticed for years, not even by the author if they only ever used the long form of the options. The third version clarifies what -s stands for, and adds a version flag so customers can figure out what version they are running (this may need to be backported into the older versions).
$ perl opt3 -s foo Sim: -1 Src: foo
Another solution would be to disallow short option names, which is probably more viable when paired with ZSH tab completion for the script (program, app), or if the program (script, app) is rarely used on the command line. Scripts used by other users or external paying customers probably need to be more formal and static about what options are supported and the forms they can take: users will do the darndest things.
If short options are allowed, then bundling should be specified, assuming the options processing code supports it. Getopt::Long can enable bundling via
use Getopt::Long qw(GetOptions); Getopt::Long::Configure("bundling"); GetOptions( ...
Formal software should probably disable bundling, and bundling may complicate shell autocompletion support, among other problems. On the other hand, bundled flags are probably easier for someone typing quickly on the command line. Tradeoffs.
Option handling libraries may not support bundling. This may make it difficult to port software that does supports bundling to something that does not.
This can be fun to write, but is usually not practical due to confusion from anyone else subjected to your one-off option handling code. Especially if remote customers will use it. In a small group and with a good reason, maybe. dd(1) is a notable case, and remains in widespread use even with a weird option handling system. find(1) and tar(1) veer into domain specific language territory. Other downsides may include difficulty writing ZSH completion scripts: more custom code. filesys/parsepath in my scripts repository uses custom options handling.
The unix shell was changed to avoid breaking dd(1); prior to that change, FOO=bar environment settings could be made anywhere on the command line.
Another case for custom code is if the standard solution is too slow; command line tools can be latency sensitive in that a user may find them bloated and annoying to use if they take too long to run. Getopt::Long is relatively heavy; it may make sense to use something faster and with far fewer features where latency is critical. Will the tool be used on older, slower systems, or on virts with minimal CPU and memory resources? In this case faster code may be advisable, especially if the speed budget has been blown somewhere else, or a user expects a vi filter to be as fast as possible. misc/remf in my scripts repository aims for speed.
https://thrig.me/src/scripts.git
tags #unix #perl