Unix command line conventions over time
blog.liw.fi
blog.liw.fi
What would the non-shell interface to commands for text processing pipelining (e.g. sort, cut, grep, etc., all of which absolutely need options to function) have looked like? Some people to this day believe that any text processing more complicated than a simple grep or cut should be done in a stand-alone script written in a domain-specific language (e.g. Perl or awk), rather than piping shell commands to each other.
Personally, I’m glad we got the best of both worlds—having command line tools with a dizzying array of options doesn’t preclude being able to accomplish the same tasks with a more verbose stand-alone script. It is often far faster to write shell pipelines to accomplish fairly involved tasks than to write a stand-alone script. The classic example is the (in)famous McIlroy vs. Knuth: six lines of McIlroy’s shell pipeline accomplished the same thing as dozens of lines of Knuth’s “literate program,” while being equally as understandable [0].
>It’s a shame because McIlroy had a lot of interesting ideas around pipelining audio and graphics that, as far as I know, never materialized.
I would love to hear more about this. The UNIX shell is amazing for pipelining (most) things that are easily represented as text but really falls flat for pipelining everything else.
[0] https://leancrew.com/all-this/2011/12/more-shell-less-egg/
Common misconception. See https://buttondown.email/hillelwayne/archive/donald-knuth-wa... ("Donald Knuth Was Framed") and discussion at https://news.ycombinator.com/item?id=22406070 (etc).
If your point is that the shell script isn't really the same thing as Knuth's program: sure, the approaches weren't algorithmically identical (assuming constant time insertions on average, Knuth's custom trie yields an O(N) solution, which is faster than McIlroy's O(N*log N) sort, though this point is moot if you use awk's hashtables to tally words rather than `sort | uniq -c`), but both approaches accomplish the exact same end result, and both fail to handle the exact same edge cases (e.g. accent marks).
If instead of writing a full program as asked, he had given some cop-out like “actually, instead of writing this in WEB as you asked, I propose you just go to Bell Labs or some place where Unix is available, where it so happens that other people have written some programs like 'tr' and 'sort', then you can combine them in the following way”, that would have been an inappropriate reply, hardly worth publishing in the CACM column. (McIlroy, as reviewer, had the freedom to spend a section of his review advertising Unix and his invention of Unix pipelines, then not yet as well-known to the general CACM reader.)
So while of course shell one-liners are faster to bang out for a one-off use-case, they obviously cannot accomplish the task that was given (of demonstrating WEB). (BTW, I don't want to too much repeat the earlier discussion, but see https://codegolf.stackexchange.com/questions/188133/bentleys... — on that input, the best trie-based approach is 8x faster than awk and about 200x faster than the tr-sort script.)
Taking 10x longer doesn't seem like a language problem. If you don't know bash well you're going to take even longer to do it in bash than in python.
In any case the task you described is pretty much the same in python as in bash. At worst the python is going to be more more verbose.
python -c "print(len(set(w for l in list(open('test.txt')) for w in l.split())))"
vs tr ' ' '\n' < file_name | sort | uniq -c | wc -lIn Python you could use a generator but it would get a little more complicated and you'd still have to add all the words to set() but hopefully the number of different words is not that great.
The trie approach is quite memory efficient and that can matter.
I think you can just omit it but yeah...
And fortunately nobody is foolish enough to think that shell scripting is robust enough to use for more than one-off uses! /s
I have no idea what the original intention was, but I could see the interface being Emacs or Vi(m).
A workflow I use a lot in Emacs with eshell is to pipe command output to a buffer, manipulate the buffer using Emacs editting commands (including find/replace, macros, indent-region, etc.), and then run another shell command on it, write it to a file, or copy/paste it somewhere else.
It's not for every situation but it's a lot faster than coming up with a complicated shell "one-liner".
Any user-space executable can issue system calls, the shell isn't special there.
Do you know if those ideas are documented anywhere?
(Not that you can’t just base raw bytes via unix shells but it’s awfully error prone).
>None of this explains dd.
This one really made me think. I have never thought about dd before. Although I have wondered about other commands. Perhaps the author should also have added some command options not requiring a minus prefix, e.g. tar, ps.
The manpage [0] reads:
In the first (legacy) form, all option flags except for -C and -I must be contained within the first argument to tar and must not be prefixed by a hyphen (`-'). Option arguments, if any, are processed as subsequent arguments to tar and are processed in the order in which their corresponding option flags have been presented on the command line. In the second and preferred form, option flags may be given in any order and are immediately followed by their corresponding option argument values.
https://www.ibm.com/docs/en/zos-basic-skills?topic=concepts-...
Edit: wikipedia agrees with your IBM origin story https://en.wikipedia.org/wiki/Dd_(Unix)#History
For some reason, this seems to be vanishingly scarce knowledge in Linux / Unix circles.
There was a time in my life I knew how to spell JCL....
There was someone on HN a couple of weeks ago who was astounded to learn that "modem" means "modulator-demodulator." Like it was some kind of forbidden magic.
Things that you an I consider entry-level knowledge are like scrolls and rune stones to tech people these days.
Radio, cable, and fiber don't carry bits. They carry waves - analog signals - just like an old fashioned copper phone line. They all need a modulator-demodulator to convert between bits and waves.
When I made the switch to working on a JAVA application running on Linux I had a hard time accepting how easy a lot of stuff was.
If you type man dd, the function of the tool will be described as "copy and convert". I can't find the video but there is an interview in which Kernighan, I think, explains that the tool should have been named cc but it was not possible because it was already taken by the c compiler.
There are more at pp. 486ff in Doug Lowe, MVS JCL (1994) here:
http://library.lol/main/6784CBC9EE9BBA5AD26993F497514661
It's still a rather rough fit, though I'm still pretty sure the connection exists.
Keep in mind that Unix dd dates to the early 1970s, and JCL itself may have evolved.
//MYJOB JOB
//FOOBAR DD DSNAME=MYFILE,DISP=(,DELETE)
//STEP1 EXEC PGM=IEFBR14
IEFBR14 is a program that does nothing at all!Although the Unix dd command wasn't patterned on the JCL command, I suspect that the multiplicity of possible options led its designers to choose the key=value option syntax that looked vaguely OS/360ish.
By the way, the - flag for options first appeared in MIT's CTSS, which was the direct ancestor (at least at the user level) of Multics.
I'll see if I can find a more canonical / complete reference.
tar is a bit messy but the worst is definitely ps. It has dash and non-dash options, and they don't have the same meaning and interact in a weird way.
Edit: the command is "ps", I somehow managed to mess up the most important word...
Sorry if it should be obvious, but "the worst is definitely the worst" … what is the worst? (Presumably not tar, unless I'm reading incorrectly.)
ps -ef | grep foo
Is what I use 98% of the time.I like the forest (tree) mode.
IIUC, other non-Unix operating systems commonly used the subcommand style for normal interactive use; it was the style used by the normal command shell, and all commands in those operating systems therefore followed this style. The operating system (or its shell) was responsible for enabling easy use of this style by adding completion support (commonly using the ESC key, IIRC). This older style can still today be seen in Unix tools like ftp and telnet, which uses this style for its internal command line interface. Another tool using this style, which many might be familiar with, is the multi-platform Kermit.
Unix therefore has been rather late in adopting this style, first in gradually adopting tools which uses the subcommand style (ip, git, etc.), and later with support in shells for programmable completion support where a program can optionally register completion hooks for the user shell to use, making it finally come up to the level of those older operating systems.
du [-s] [-a] [name ...]
ld [-usaol] name ]
ls [-ltasd] name ...
pr [-lcm] name char *argv[] = {"my-program", "--config-file", config_file, NULL};
run_command(argv);
With only `--config-file=x`, you would have to allocate memory for the option: char *config_file_opt = asprintf("--config-file=%s", config_file);
char *argv[] = {"my-program", config_file_opt, NULL};
run_command(argv);
free(config_file_opt);
And it becomes even more hairy if you A) want to avoid heap allocation in most cases or B) want to use standard functions rather than asprintf. You'd have to do something like this: char config_file_opt_buf[1024];
char *config_file_opt;
size_t config_file_opt_len = snprintf(config_file_opt_buf, sizeof(config_file_opt_buf), "--config-file=%s", config_file);
if (config_file_opt_len >= sizeof(config_file_opt_buf)) {
config_file_opt = malloc(config_file_opt_len);
snprintf(config_file_opt, config_file_opt_len, "--config-file=%s", config_file);
} else {
config_file_opt = config_file_opt_buf;
}
char *argv[] = {"my-program", config_file_opt, NULL};
run_command(argv);
if (config_file_opt != config_file_opt_buf) {
free(config_file_opt);
}
And even that ignores any kind of error checking of snprintf or malloc which may fail. I also wouldn't even be comfortable assuming the code I just wrote is correct, without thinking long and hard about it and reading standards/documentation thoroughly. Whereas the first example is so simple it's obviously correct.I think we should keep `--long x`. If anything, `--long=x` is the one which should go. There will always be ambiguity; the meaning of something like `-ab` also depends on whether `-a` takes an argument or not.
Really though, I think there are enough situations where `--long=x` is useful that I think keeping both versions makes sense.
The fact that this is cumbersome in C is fixed by fixing C, not by forcing a pattern onto the ecosystem that only makes sense to work around C's deficiencies.
The argument that you should just heap-allocate because it usually doesn't matter in contexts where you'd spawn a process anyways is a much better argument though. Still, I find the `{"whatever", "--config-file", config_file}` approach much more elegant.
For example, someone might write:
args = [cmd, f'--foo="{foo}"']
and expect the surrounding quotes to be stripped off, but that'll only happen if the spawn goes through a shell first. Some argument parsers may also try to strip quotes off, but I don't believe there's any consistent behavior guaranteed around that.If you're that worried about performance about a few string allocations, you shouldn't be passing around strings anyway, shell or not.. And simply call functions from the same process and use for example the file descriptors you already have.
You could also just simply pass a binary blob (messagepack) as one of the arguments, if that's your thing.
Just wondering: why `--long=x` over `--long x`?
It only lacks in being able to work with brace expansion to facilitate specifying multiple values.
--long={foo,bar}
becomes
--long=foo --long=bar
For the problem you mention, I normally have them separate while I use completions, then add the = sign after.
--option{X,Y} -> --optionX --optionY
--option:{X,Y} -> --option:X --option:Y
...EDIT: also why is `ps` default output so useless? With no arguments it only prints a couple processes and that's it. I have no idea what it's supposed to show me. Talk about bad UX.
Now, my X sessions always have a dozen xterms running and plain ps isn't very useful, but it might break scripts if they changed it...
I think you could say this for CLIs in general. While we can all agree that the CLI is great for tasks that require repetition; it's just a PITA for any one-time task. When you just need to do something once and as soon as possible, the manual-help-type-run loop gets tiresome quickly.
Passing options to a program so it can do what most users would want it to do in the first place is nonsensical, and yet that describes the behaviour of many if not all of *nix's tools. It should be the other way around: do what is expected by default, and provide options for different, more uncommon use-cases. Though I suppose this is also a form of baggage from the past that it's very difficult to get rid off because of backwards compatibility.
The exact same holds for ffmpeg command-line.
$ find -type f Data/
find: paths must precede expression: `Data/'
find: possible unquoted pattern after predicate `-type'?
sighGNU keeps a list of them, so that anyone implementing a similar functionality should use the same option if it already exists:
https://www.gnu.org/prep/standards/standards.html#Option-Tab...
E.g.
find /path/to/dir -type f
works, while find -type f /path/to/dir
does not.The clearest proof that it's a pipeline is probably how it interacts with `-exec`: `find . -type d -exec echo {} \;` will print only directories, while `find . -exec echo {} \; -type d` will print both files and directories.
Regardless though, I sure wish they would let you specify the directories at the end.
find and test: How To Read And Write Them
https://www.oilshell.org/blog/2021/04/find-test.html
tl;dr The args AFTER the root dir args form a boolean expression language -- I call it the "I'm too lazy to write a lexer" pattern
find /tmp -type -f -a -name 'foo*'
test is the same way, e.g. test -f /tmp -a -f /bin find /tmp -type f -a -name 'foo*' # no "-" for the -type
this is bitten me more often than I'd care to admit.I honestly wonder where this wonky syntax came from ... i.e. was it a Bell Labs thing or later
tesseract imagename outputbase [options...] [configfile...]It looks especially silly, when long options are used to encode some mini programming language (DSL), like in imagemagick and ffmpeg, or when they are used to represent some associative data, like in case of the AWS CLI.
If -- would be a : AFTER the option name, it would look like Objective C named arguments or set-words in REBOL and Red.
If -- would be a : BEFORE the option name, it would look like keywords in Lisps.
As a side-note, shells got quite evolved, actually, but the momentum of the mediocre bash-line prevailed, sadly.
I urge people to learn about es, the Extensible Shell: https://wryun.github.io/es-shell/
Its ancestor, the Plan 9 rc (which I think stands for Run Command) and it used "; " as a prompt, so u could combine multiple commands easier, by copy-pasting whole lines, instead of fiddling with the exclusion of the varying length prompt.
Both of these shells have a lot better string quoting rules than bash, so they play nicer with filenames containing special characters.
They also follow the unix philosophy closer, by not necessarily providing line editing capabilities, which can be added by running them thru eg. rlwrap or not really needed, when running them from Emacs or Plan 9's acme environment.
I would also highlight, that es is just 163KB, while bash is 1.3MB, yet it provides a magnitudes more powerful, dynamic, garbage collected, functional programming language!
Any option character excludes a set of inputs, and we want this set to be as small as possible. It is already hard to look at file named "-f" -- do you want to also make accessing files named ":f" (or even worse, "f:") harder? Thus the desire to keep the first "-".
Now, they could have used "-:" or "-=" for long options.. but I think "--" won because it is so easy to type -- no need to move your fingers.
Using long options to encode DSL, like imagemagick and ffmpeg, can be pretty silly.. But those programs are exceptions, and most of the option usage is of much simpler variety. Like "grep --label=input --color=always".
The comments about es sound weird... You can set ";" as prompt in sh/bash too. I would not call its quoting "nicer" -- yes, using '' for a single quote is nice, but lack of " support sounds like a bad omission. Yes, bash is pretty fat by 1990's standards.. but if you want a real programming language with no line editing capabilities, no need to reach for obscure shells -- grab perl, python, tcl, lua or any of the other scripting languages.
rc and es is an good example of what improvements can be achieved, if we challenge historical decisions and get rid of the not so great ones.
The "; " prompt was just a historical side-note, because I saw someone asking about it the other day on twitter: https://twitter.com/thingskatedid/status/1316081075043463170...
By the quoting rules, I meant, that if you have a variable (eg v='a b') with a value containing whitespace, when you reference it ($v), then you don't have to worry about quoting it in any way, because the space in its value won't cause any problems.
%/c/plug-in/video.r
%//sound/goldfinger.mp3
%"/c/program files/qualcomm/eudora mail/out.mbx"
%c:/docs/file.txt
%\some\cool\movie.mpg <= understood backslashes too, so it worked in DOS/Windows too!
%cool%20movie%20clip.mpg
Source: http://www.rebol.com/docs/core23/rebolcore-12.html#section-2...
then you could mix them with `:get-words` and `set-words:`. Rebol also had an interesting take on options - with and without arguments -; it called them refinements and you would just stack them with slashes onto the "command". the refinement arguments were added to the end of the parameter list though, so it was not exactly obvious which argument belonged to which refinement, but in practice it worked quite nice, surprisingly:
str: "test"
insert/dup/part str "this one" 4 5
print str
=> this this this this test
and reversing /dup and /part:
str: "test"
insert/part/dup str "this one" 4 5
print str
=> thisthisthisthisthistest
Source: http://www.rebol.com/docs/core23/rebolcore-9.html#section-2....
getopt is lipstick on a pig.
dashed argument are ugly and hard to read.
The worst offenders, programs that implement a language in the args. if you do this just build a proper parser and leave off the dashes. the worst offenders.
iptables find megacli
megacli, remember that? it was the control application for lsi megaraid cards, it was the program that turned me off getopt style args. when I figured out that the megacli parse let you drop the dashes(and the obnoxious capitalization) it was like a breath of fresh air in a fart filled room.
So a better way to do args? use dd style args. Yes dd, dd had it right all this time, I find "key=value" style args to be superior to "--key value" and don't even get me started on the absurdity of "--key=value"
But I accept why linux command line is the way it is - its a shark, not a dinosaur
One oddity not explained is tar allowing multi options crammed together without any dash, that nobody can ever remember (eg. tar xvf archive.tar)
This is a BSDism, and it's deprecated even there. bsdtar allows it for compatibility with old scripts.
GNUtar has long-switches for all options, which makes it considerably easier to use and understand, for example:
gtar --create --file etc.tar --verbose /etc
No, it was like that when Tar was first introduced, as part of Seventh Edition Unix:
> tap [key] [name ...]
> The function portion of the key is specified by one of the following letters ...
For example, "x" to extract.
> The following characters may be used in addition to the letter which selects the function desired
For example, "v" for verbose.
These have gone a long way for me:
tar xzf <in> eXtract Zipped File
tar czf <out> <in> Create Zipped File
Anything more complicated and I have to google.For many years, NetBSD tar has autodetected bzip2 compression.
tar xzf 1.tar.bz2
will work on gzip as well as bzip2. Whereas GNU tar still requires "j" instead^2 tar xjf 1.tar.bz2
1. For example, FreeBSD or MacOS tar is BSD tar. It will autodetect bzip2 compression.2. The GNU tar included with VoidLinux still requires z or j.
The pax(1) utility is the POSIX solution to these incompatibilities.
The following should work, it worked on my Arch Linux install (but note that auto detection is not a "new" feature to GNU tar, I've been using it for at least 5 years)
echo "Hello" > foo.txt
tar cjf foo.tar.bz2 foo.txt
rm foo.txt
tar xf foo.tar.bz2
cat foo.txt
and produce "Hello"To be clear, I'm not saying the following should work
tar xzf foo.tar.bz2
What should work is: tar xf foo.tar.bz2
And for proof this is GNU tar $ tar --version
tar (GNU tar) 1.34
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by John Gilmore and Jay Fenlason. bzip2 -dc 1.tar.bz2|tar xf -That’s considerably more cumbersome than tar -cvf.
Perhaps it comes down to what programs one uses routinely. Typing "tar xzf" multiple times per week I am unlikely to ever forget it.
IIRC, they liked the plus, but changed to use the double dash to be POSIX compatible (which presumably requires options to be preceded by a dash).
For long options, there's the convention of `--no-foo` meaning the opposite of `--foo`, but `+e` hasn't really caught on for short options besides the shell ones.
Being able to negate options, letting the last one win, is useful to be able to define default options in aliases. For example, it might be useful to be able to do this:
$ alias less='less -X'
$ less +X foo.txt
or $ alias grep='grep -n'
$ grep +nH -r foo *The set command is a great example, "minus e" means "enable error checking" and "plus e" means "disable error checking". That doesn't make sense, that's not what minus and plus means.
The fact that they seem backwards seems like something that's easy to get used to over time. It's like electrons being negatively charged.
To get used to it, one could think of it as a crossed-out dash instead of a plus. Crossed-out means that the dash is disabled.
> This is why the nice number is usually called niceness: a job with a high niceness is very kind to the users of your system (i.e., it runs at low priority), while a job with little niceness hogs the CPU. The term "niceness" is awkward, like the priority system itself. Unfortunately, it's the only term that is both accurate (nice numbers are used to compute the priorities but are not the priorities themselves) and avoids horrible circumlocutions ("increasing the priority means lowering the priority...").
Jerry Peek et al. in Unix Power Tools (O'Reilly, 2007, p. 507)
via https://stackoverflow.com/questions/14067128/why-are-nicenes...
There is this problem I face really often with that I semantically know all the small operation I want to run on command line but some of them have branching element in them. Often I just want to reconverge my data and run more commands. As one can imagine your writing in a functional way acting on raw data always but it's hard for other people to see a shell command or a bash script and really internalize the underlying operation.
It would be really fancy if you could take in a command line opt and generate a corresponding flow diagram too.
Unless you were thinking about something else.
Also, I just noticed: they have `-nostdinc` for C, but the C++ equivalent is `-nostdinc++`, which looks like "no stdin C++".
> The --email bit is a joke.
what's the context of the joke?
I assumed it meant to send the output of the command as mail. That's vaguely useful instead of piping to a cmdline MUA. I couldn't believe I'd been missing this for 30 years and so I tried it on few things like
ps aux --mail=me@domain
error: unknown gnu long option
Looked in my inbox. Nothing.
Then I got to the bottom of the page for the punchline.
grrrr
I wonder where I can find more posts / articles like this. short, punchy, well written histories of software I use.
Text says: "it would be given some number of filenames as command line arguments, and it would read those ... Options didn’t exist".
Example shows: unneeded usage of `cat` as if `wc` is non-standard utility and cannot process file names by itself, option for `wc` is used. It contradicts text in both ways!
The idea, though not the syntax, came from the installation of a TOPS-20 machine and some VMS machines on a lower floor. Our own homegrown OS (ITS) used control characters as commands and had DDT as a shell.
> In the beginning, in the first year or so of Unix, an ideal was formed for what a Unix program would be like…
Note that the approach you’re talking about came straight from the Multics of that era, though Multics already supported some short options at that time.
> None of this explains dd.
Because just as Unix in many ways copied Multics, the dd command copied IBM.
I think distinct shell commands are good. MH is my go to instance of doing this right (it's still under active development)
1. no-valued option: --example 2. single-valued option: --key=value 3. multi-valued option: --elements:1st --elements:2nd
Short options are ruled out, although it accepts the short option -h for discoverability.
I particularly like the repeated option pattern for multi-valued options. This avoids ambiguities such as:
cli --elements 1st 2nd arg
Yes, the ambiguity may be removed by using doubled dashes. However, this is error-prone.I often wondering whether a more familiar syntax for the second and third kinds could be adopted. For instance:
--key value
--elements 1st --elements 2nd
[1] https://github.com/evanw/esbuildSome tools still use +options, like dig(1).
For example, `set -e` enables the `e` option (exit script immediately upon seeing a nonzero exit code). Guess how to disable it? Yup, `set +e`
1- short: http://mywiki.wooledge.org/BashFAQ/035#getopts
2- long: http://mywiki.wooledge.org/BashFAQ/035#Manual_loop
I don't support `--option=` mode. I don't support giving options after arguments. I prepend info and errors with braces like : `[TAG] message` and print them to STDERR.
Opinions?
However, the way history has unfolded, I think we’re stuck with this mess for good.
Perhaps Apple's biggest contribution to the Unix/Linux landscape was launchd, which inspired systemd. I don't know if we would've endeavored in rewriting essential and mostly working decades-old parts if there wasn't a proven path already laid out.
Maybe LLVM as well, a modular compiler toolchain, but that's a strech.
Sun's only good idea for desktop UNIX was NeWS and they killed it. Their desktop execution for Swing proves how much they understood (not really), desktop development.
As for Apple and UNIX, it was more the inverted takeover from NeXT than anything else, given A/UX execution.
Also Steve Jobs wasn't a UNIX fan, it was more of a embrace POSIX due to the competition with Sun, and extend with NeXTSTEP Objective-C frameworks than anything else.
Get hold of his USENIX talk about "UNIX should be invisible".
What I'm saying that Apple is a company usually willing to rewrite things the “right way”, vide Webkit, LLVM.
WebKit was born as Khtml by KDE project.
I don't know how that proves that it's not a company willing to rewrite foundational/taken for granted stacks.
Being huge and powerful is no guarantee of success when it comes to shipping software. Size can often be a hinderance.
They are on a similar position nowadays, trying to fix the UWP disaster, maybe by a future Windows 12 we get what Windows 8 should have been all along.
Likewise, starting from the same premise that Apple wouldn't be bleeding money, they would been able to keep up with their roadmap with System 8 and integration of Copland technologies.
However the main challenge, memory protection, didn't. Much like Longhorn's file system.
In any case this is playing guessing games, what would have happened if Apple wasn't begging for money.
So who knows how System 9, 10… would have turned out.
As for the Longhorn example, part of it did ship, as SQL Server features and some ideas on ReFS.
And that this was a missed opportunity for the whole industry as it's probably not going to happen anytime soon.
Is it really difficult to learn that e.g. "rm" stands for "remove"? I've known very few people who found it difficult to learn the key things very quickly.
And not having to type (and spell) full words is a relief. (I know a shell can take care of spelling, but not all unix commands are entered interactively.)
Still, the article is interesting, and worth reading.
As another example : the -v option. In most tools this means "verbose". In some tools, but not all, you can pass it multiple times to increase the level of verbosity. But then there are a few tools, e.g. top, where -v seems to mean "version" [0]. Or take the -h option. On many tools it stands for "help". But on tools like ls or sort, it stands for "human-readable".
To me personally this is not an issue - I know by heart a few combinations of options that I use frequently, and I know where to find out the details of those, as well as other options I use less frequently. But I can see how it would be easier for people to pick it up if those things were just a little more standardised.
This version of ps accepts several kinds of options:
1 UNIX options, which may be grouped and must be preceded by a dash.
2 BSD options, which may be grouped and must not be used with a dash.
3 GNU long options, which are preceded by two dashes.
And:Note that "ps -aux" is distinct from "ps aux". The POSIX and UNIX standards require that "ps -aux" print all processes owned by a user named "x", as well as printing all processes that would be selected by the -a option. If the user named "x" does not exist, this ps may interpret the command as "ps aux" instead and print a warning.
Edit: On my system, "ps -aux" does the same thing as "ps aux" and no warning is printed, despite the what the man page says. I'm not going to try to create a user named 'x' though.
PS> gcm *build*
CommandType Name Version Source
----------- ---- ------- ------
Alias Build-Checkpoint 5.8.6 InvokeBuild
Alias Build-Parallel 5.8.6 InvokeBuild
Alias Invoke-Build 5.8.6 InvokeBuild
Application mcbuilder.exe 10.0.2200… C:\Windows\system32\mcbuilder.exe
Or find everything from a module: PS> gcm -m InvokeBuild
CommandType Name Version Source
----------- ---- ------- ------
Alias Build-Checkpoint 5.8.6 InvokeBuild
Alias Build-Parallel 5.8.6 InvokeBuild
Alias Invoke-Build 5.8.6 InvokeBuild
And at least if you use `MenuComplete` for tab (`Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete` in your profile), you can also just write `*<noun>` and hit tab. It will offer completions regardless of the verb.See especially getsubopt(): https://www.gnu.org/software/libc/manual/html_mono/libc.html...
For example,
-v, —-version
-o <file>, —-output=<file> if <file> is - then use stdout
-i <file>, —-input=<file>. if <file> is - then use stdin
-h, —-help
Or is it just anarchy out here?
Another common option I am thinking about is -r for --recursive.
I could advocate to use only commonly accepted short options.
-a, --all
-d, --debug
-f, --force
-h, --help
-o, --output
-p, --port
-q, --quiet
-u, --user
[1] https://clig.dev/#arguments-and-flags