Why do long options start with two dashes? (2019)
blog.djmnet.org
blog.djmnet.org
find is annoying, though. I'd encourage you to check out fd.
The obviously right choice would have been [ ]. You know, like in
if [ $foo ... ]"[" is just another name for the "test" command. It isn't special syntax.
(https://golang.org/pkg/flag/#hdr-Command_line_flag_syntax)
Edit: To be clear, I'm mostly "blaming" Go for re-popularizing this style by a) putting it in the standard library and b) being a widely used programming language; I'm not saying Go came up with this or anything.
(idk about the space separated args tho that's even worse)
Go was designed by former Bell Labs people who worked on Unix, Plan9, or both. many things about Go that people attribute to "googlism" is really attributable to work done at Bell Labs.
Is it based on Google's internal preferences?
We're 50 to 30+ years away from that Bell Labs work. They could have checked what happened in the meantime with the rest of the computing world, before re-imposing obsolete ways with the full power of Google behind them...
The main design motivation of absl::Flags is that the flag definitions can appear in any module, not just main. Go inherits this. A quirk that Go did not inherit is gflags --nofoo alternate form of --foo=false.
This is all documented at https://gflags.github.io/gflags/#commandline, which is pretty much a verbatim export of the flags package documentation that a Google engineer would see internally.
Well that's kind of horrifying. That means that command-line arguments are a form of global state, and can silently alter the behavior of the program without the calling scope noticing.
I'm kind of vary of these mechanisms, because I've been bitten by them before. There was a python library I used that read its configuration from sys.argv the first time an object from the library was constructed. I had a rather painful time debugging to find that my script accepting a -b argument resulted in the library switching to batch mode and suppressing all graphics. Dang it, those were my arguments, and the library had no right to go behind my back and look at arguments that hadn't been directly provided to it!
VLOG(2) << expression_with_side_effect() << " LOL";I'm almost scared to ask. How is that even implemented?
!VLOG_IS_ON(level) ? (void) 0 : [a hack to stop compiler warnings] & LOG(INFO) << ...Not sure about the relevant point on compact short options syntax as in `tar -xvzf archive.tgz` though... (edit) after a quick & sloppy test it seems to work as expected
In my defense, the specific example I gave is valid for both GNU and BSD versions of tar. If I understood correctly, the issue you point to (order among short form flags) is related to the fact that `f` expects an argument and consequently has to appear in the last position.
I personally wouldn't touch that, but that's related to my allergies to JS ecosystem and predisposition to panic attacks when I see stuff like that https://github.com/tldr-pages/tldr/blob/master/package-lock.....
TIA if you could explain; is it native zsh or a plugin?
I haven't yet had a computer ask for clarification when I used tar or dd in an uncommon and destructive way.
Which is faster and probably safer than scanning the documentation for individual flags and hopping you got the nuances right...
See, the two cases aren't:
(1) Thoroughly study man page -> (2) Become expert at the command's options (3) try command secure in your mastery of it
vs
(2) Check tldr examples -> (2) try command
They're rather:
(1) Open man page, (2) scan and skim the man page and the dozens of irrelevant flags, caveats, and obscure options, until you find some flags that look to do what you want, (3) half-read them, (4) try command
vs
(2) Check tldr examples, (2) find an example that does what you want (which is usually one of the covered use cases) (3) try the command using the example syntax
Fortunately, there are alternative packages that does.
It's a convention-over-configuration thing I think. I mean they set a standard, so you can move on. The alternative is to sit and think and discuss about what order to put your documentation in.
So you read the whole man page when you need a flag that does something specific or do you mean you never write new things and just have to look up flags already in use by some script? Because for everything else that seems like a fascinating waste of time.
I had the impression only CLI tools from the Java world are that strange.
https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html
If it mattered much, I'd expect GNU to be internally consistent.
GNU's conventions are generally complimentary, but not incompatible with POSIX. And POSIX specifies the behavior of the sort of flags cc(1) should understand[1].
There are many POSIX and other traditional *nix tools that are a convention unto themselves for historical reasons. E.g. notice how GNU "dd" doesn't follow normal GNU command-line conventions either.
1. https://pubs.opengroup.org/onlinepubs/7908799/xcu/cc.html
vs. Unix where char *argv[] is what makes it to the syscall layer.
The result there is that command line processing is more consistent program-to-program on Unix. On Windows, every program could decide to tokenize the arguments differently.
Worse, even Microsofts two implementations (CRT and WINAPI) disagree: https://github.com/rust-lang/rust/issues/44650
CommandLineToArgvW - You called that "WINAPI", but it's worth mentioning the more specific provenance of shlwapi.dll. This is not a core, foundational part of Windows that is used in core, foundational things. It's a helper function from the shell (explorer, not shell in the Unix sense). So, while it has a look and function that seems pretty foundational, it really isn't. It's there because somebody working on Explorer long ago found that useful to have and decided to export their helper function in the DLL.
CRT - A CRT binary ships with Windows, but really, that code is maintained and distributed by the compiler guys and DevDiv. So theoretically, the argv parser could change at those people's whim alongside a new Visual Studio release. And it seems from squinting at that github issue like that might have happened here.
So really ... there are more artifacts here attesting to the fact that the command line arg parser is not part of the operating system. People find that functionality useful, so they look for things that "look like" the operating system official method, and maybe they find stuff that does "look like it" -- but such a thing isn't really there.
Your reply might still make sense (i.e. untar could automagically figure it out), but I was highlighting how tar/untar today also means (de)compressing that tar archive using many different compression formats.
foreach a in arglist
if a=~/^-(\w+)(?:=(.+))?/
$opts{$1} = $2;
else
push @pos_list, $a
There is no need for the dependency and complexity of `getopt` library.And for the user side, no more cryptic ninja arts. The only trick user need to learn is the shell alias and functions.
In your regex at least, removing the confusion is simple as adding another ‘-‘, and now you’re in compliance with the expectations of almost every IT person in the world who uses Unix command lines.
However, even those seem to raise some questions, for example:
> To make command line even more confusing, multiple tools I have used allow spaces in optional arguments (e.g. "-opt1 arg1 arg2 -opt2", where arg1 and arg2 set two values for -opt1).
Is described as something that's permissible:
> An option and its argument may or may not appear as separate tokens. (In other words, the whitespace separating them is optional.) Thus, ‘-o foo’ and ‘-ofoo’ are equivalent.
Therefore the below would be considered equivalent:
-opt1 arg1 arg2 -opt2
-opt1arg1arg2 -opt2
Were your expectations different?Are there any good articles on the benefits of following such rules (any fungible improvements to legibility or usability, as opposed to just "consistency amongst different tools")?
Are there any tools which can validate whether any piece of software conforms to this standard (either by scanning the man pages, or the code, or a formalized format of parameters the app supports)? Personally, the closest i've found is Typer ( https://typer.tiangolo.com/ ) but without anything that can automatically reject non-conformant code as a part of a CI process, i think enforcing such formats would be a non-starter for me.
I’m also glad short opts are available for my personal day-to-day work. I spend most of my time in a a terminal and appreciate having short-hand available.
test -f foo -a -f bar -o -z foo
can be read isfile('foo') && isfile('bar') || emptystring('foo')
Likewise find . -name '*.py' -a -executable -o -printf '%P\n'
can be read (F.name matches '*.py') && (F is executable) || print('%P\n', F)
where F is the current node in the file system traversal.They both respect -o as OR, ! as NOT, and ( ) for precedence, which you have to quote as \( and \).
A couple years ago, someone helped me implement a better "find" without this wonky syntax for https://www.oilshell.org/ . But it isn't done and needs some love. If anyone wants to help, feel free to join Zulip :)
I do think that "find" is more like a language than a command line tool. It's pretty powerful, e.g. I just used it to sort through 20 years of haphazard personal backups.
jq has a lexer and hence a "real" syntax, but so does awk, which is maybe 30 years older. But yes jq is a surprisingly big and rich language, maybe bigger than awk:
$ find -name something
find: illegal option -- n
usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]
find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]
What does "illegal option" mean exactly? Why is it "n" which is the first letter of "-name"? Yes, it wants a path. Yes, even if you want to search in the current directory. Yes, it IS unusual, because all other commands that operate on directories, like `ls`, assume current directory if you don't specify any.Why could it not just say "a path is required" instead?
Judging by the usage message you printed, you were almost certainly using a BSD implementation, probably on macOS, which in turn is probably sync'd from FreeBSD. `find -name something` will fail early in main. See https://github.com/freebsd/freebsd-src/blob/b422540/usr.bin/... When processing the 'n' in '-name' getopt() will return '?', which will end up calling usage().
The GNU implementation of find is completely different, though I'm not sure it does what you expect:
$ find -name something
That prints nothing and returns a successful exit code. But if you remove the "something" operand you get what I presume you were originally expecting as an error message: $ find -name
find: missing argument to `-name'
But try deciphering the option processing of GNU find to understand why it behaves that way: https://git.savannah.gnu.org/cgit/findutils.git/tree/find/ft... Hint, see https://git.savannah.gnu.org/cgit/findutils.git/tree/find/ut...Not rocket science, but as a programmer and maintainer which approach do you think makes more sense? Is trying to do the supposedly intuitive thing worth it, especially considering find's already arcane and irregular syntax? As an experienced command-line user I'd just be thankful that the option flags (as opposed to the filter directives) are parsed regularly.
It's not like the source code is now etched into stone and can't be changed. Or is it?
Several BSD commands are pickier than GNU commands about option order, sometimes for good reason, sometimes because it was easier to write that way.
That would be a terrible shell. Changing directories, listing them, moving files, running programs are all simple no-brainer operations in any reasonable shell, but are non-trivial in any programming language that's not designed to be a shell.
tar xvf foo.tarI've seen that pattern before, but it always drives me a little crazy.
Alternatively, it could offer a y/n prompt.
That's what happens I guess when the people designing it haven't actually used a CLI day to day much, because, well, they're using Windows.
The "*" can even be in the middle. I open VS solution files all the time from Powershell. Since there are often many other files and folders with similar names alongside them I just type ".\*.sln" and hit tab.
The short terse commands and the really awkward, confusing, mistake prone syntax of sh or bash really reels their ugly head in scripts.
Interactive shell? No problem. But that's the beauty of PowerShell: verbosity and correctness in scripts, where the IDE quickly expands those long commands, and short aliases for interactive use.
Seems like the real solution is separating scripts from interactive use.
When used in an interactive shell short commands save time and effort. And it is easy to learn and remember them because in everyday work you need only about 10 commands. For some some commands which I use a lot I have one-two letter aliases to type even less e. g. i=fgrep.
It makes shell scripts less readable for someone who come from windows and and don't know even common shell commands, but for someone who use shell at least from time to time it should be easy to read.
But the biggest thing I'm happy about WRT Powershell is that it's consistent (and pretty well documented). At least it makes sense. Batch scripting really didn't.
The long names are the official readable names for scripting. It can and does have short aliases like "rm" that you would use in interactive mode.
> And what's with all of the capital letters?
PowerShell is case-insensitive. The capital letters are for readability.
Simple. All these commands work with providers, of which a file system is just one. Other providers include Windows Registry, environment variables, certificate stores, functions and variables in PowerShell runtime. More providers can also be created and plugged into the system. PowerShell Providers are essentially Window's FUSE. See [0] for details.
So, for instance, you can do `Get-ChildItem HKCU:` to list entries under HKEY_CURRENT_USER in the Registry, the same way `Get-ChildItem C:/` will list you top-level items on the C: drive. Worth observing: while the console output for these two commands is similar, the results are in fact different objects underneath (Microsoft.Win32.RegistryKey vs. System.IO.FileInfo).
In short, these commands are an abstraction over file-system-like things. Whether or not that was a good idea is a different question.
--
[0] - https://docs.microsoft.com/en-us/powershell/module/microsoft...
Because if you don't care about chaining together short flags and just want to use two dashes for your long flags, Go will happily accept that.
https://golang.org/pkg/flag/ : "One or two minus signs may be used; they are equivalent."
I ran into a related issue a couple of years back where people were using single-dash flags for a C++ project that was using Abseil flags in conjunction with getopt parsing of short flags (for legacy reasons). Why were they using single-dash flags, despite that not showing up anywhere in our documentation? They copy-pasted from --help.
(I'm happy to say that --help in Abseil has since been fixed.)
Also, that first issue happens with POSIX flags (with the GNU long flag extension, anyhow): `grep -help` is different from `grep --help` (and if you type the former, it'll just wait patiently for you to close stdin).
You mean around 2002-2006? I find that pretty hard to believe.
In longer words: Windows was originally a GUI system on top of DOS which was influenced by CP/M. The NT kernel did away with DOS, but the influence still lives to this day. For a simple one: not being able to name a file "con" (or any capitalized variation) comes all the way from CP/M.
For the uninitiated: OSes from that era didn't have "directories"; Everything lived in the root of the drive, including device files. So, to print a file, you could literally do something like:
A> type FILE.TXT > PRN
When DOS added directories, they retained this "feature" so programs unaware of what directories were could still print by writing to the `PRN` "file". Because of "backwards compatibility", NT still has this "feature" as well.Early versions of MS-DOS made it a user preference option in the command.com interpreter, whether the user wanted to use / for options and \ for path separation or vice versa.
Because they really didn’t.
I always try to design my tools with a "terse" output that makes it easier to pipe it into other programs.
Seeing functions aliased to their POSIX names is already a little bit misleading when you realize they are not a drop-in replacement at all.
find dir -type f -name "*.h" -print find <options> <paths> <expression>
Those thing you list are part of the <expression> part of the command. The <options> part in BSD find, and I believe GNU find, only uses options of the form -X where X is a single character.It's a little confusing because the man pages for both BSD and GNU find do call some of the things that appear in the <expression> part of the command "options".
> There were a few programs that ran on Unix systems and used long option names starting with either - or no prefix at all, such as find, but those syntaxes were not compatible with Unix getopt() and were parsed by ad-hoc code.
Usage: pr [+page] [-columns] [-h header] [-w with] [-l length] [-nt] [files]
Mixed in this case, with -columns and +page, and all hand parsed. But long options nonetheless.to format for printing starting at page 4, on a wide 172 colunn paper?
This was likely before the effects of Eternal September began destroying the public Usenet, so the vote may well have been held there, in one of the newsgroups relevant to the FSF, GCC or GNU.
The '--' alternative won overwhelmingly, as I rememeber it. A few hundred votes were cast by email.
IE a line to indicate use of this option and a line with a strikethrough to indicate it has been "struck out" as an option.
But using the conventional minus and plus symbols is indeed confusing.
things like this are how I know that there are too many people who are absolutely insane and who make mundane decisions.
- to add
+ to subtract
absolute genius. is this what higher education teaches people? I didn't go to college, and maybe that was best.Most other languages didn't want to handle the syntactic ambiguity of using the period as a decimal point and a statement separator.
For Prolog/Erlang, I think the preceding syntax is disambiguating enough
It's incredibly simple. Comma means "and", semi-colon ends a clause, full stop closes out the entire thought.
Oh, now this is the last thing, gotta take off the ; or replace a , with a .
At least when I was starting out, I'd have loved a more C-like syntax with {} and consistently semicolons. Of course, Elixir came and just got rid of most punctuation, which I like less.
Anyway, it's consistent and after a couple weeks of messing it up, I can consistently see where the mistake is from the compiler error; after several years, I still sometimes mess it up, but oh well. I can't recall having messed up the punctuation so much that it still compiled but wasn't what I meant, so it's almost always quick to recover.
But if you're producing a list of statements, isn't a statement-list-joining punctuation mark the perfect thing to use?
There's also the difference in different languages between statement separators and statement terminators, but I don't really know enough about it.
%p locale's equivalent of either AM or PM
%P like %p, but lower casePresumably it was rejected because the whole point of POSIX was to consolidate, regularize, and simplify pre-existing practice. Adding "+" as an additional standard option signifier would take a huge step in the complete opposite direction. The only precedence for "+" would have been the `set` shell builtin, and AFAIU the committee only begrudgingly grandfathered that syntax.
Someone elsethread mentioned the `date` utility, but if you look at the BSD implementation "+" isn't used as an option marker, per se, but rather to disambiguate operand strings. The 2001 standard only defined the following:
date [−u] [+format]
date [−u] mmddhhmm[[cc]yy]
It's splitting hairs, but POSIX was at least able to shoehorn the legacy syntax into a more regularized base interface. mytool infile=foo.fits outfile=bar.fits
Some parameters need to be given (and will be prompted for if not given), and some get a default if they are omitted. The extra tricky part is that the parameters and their defaults are read from .par files in a path. There are the default ones for the tool, and a user-specific parameter file which can be modified using the "pset" program (or read with "pget"). A tool when run will also modify the par file to update various parameters with the ones given on the command line.Unfortunately, there is no form of locking on these par files, so one has to mess around with the path settings (to make per-process paths), or use some form of locking, to ensure they don't get corrupted if multiple processes are run at once.
EDIT: Trying to remember examples of it; here's one I can recall: http://www.nynaeve.net/?p=180
It’s hard to imagine that the philosophy of these early heroes has so pervaded the world. Free software is everywhere. Other fields don’t do this to the degree software does. So much value for humanity just because the first few took one approach when another would have worked just as well.
sudo --with-wheel-group
has not (yet) been sneaked in to any major *nix distribution. `grep bar -w -- -foo-file`https://pubs.opengroup.org/onlinepubs/009696799/functions/ge...
but no 'getopt_long' (AFAICT).
OpenBSD, just as one example of a true unix-derived system, added it's own getopt_long only in 3.3 release which was 2003, ~17 years after the 1st posix in 1988. This article mentions getopt_long originating ~1990, after 1st posix standard.
date +'%Y-%m-%d-%H-%M-%S'Your example is a format string. date needed to tell if the positional argument is a format or a new date, and to make it easy, they decided to prefix the format with a special character. I am going to guess that this character should not be - (to avoid confusion with options), or numeric (to avoid confusion with new date), not have special meaning in common shells (to keep quoting simpler). They could have chosen : or ^ for example.
Not sure what you are disagreeing with though, I made no claims that someone had to use +, or positional argument in general. Just an observation about what might have guided the design process.
ls -la == ls -l -a, but then you can't ls -all because it's the same, you need --all (or +all).
Java supports -cp or -classpath but in both cases the token after the - always identifies a single option, so you don't need the second dash.
Maybe because java was intended to run on multiple operating systems, although both look out of place on MS-DOS!