15 ms·
Exploring CLI Best Practices
- okdana 10y ago>Provide long, readable option names with short aliases. It's unnecessary, and potentially even burdensome, to provide short aliases for less commonly used options. `ls --quoting-style` doesn't need a short option — you're almost never going to need it. >Provide a version command that can be accessed by version, --version or -v. Don't use `-v` for that. It's true that some utilities do, but i think even more use `-V` — including most GNU tools, Python, PostgreSQL, cURL, OpenSSH, iptables, iproute2, procps-ng, just about any Symfony Console app, &c. Even if you don't need a verbosity option right now, you might some day, and it's best to have that work as expected. >Sometimes your script will take longer to execute than people expect. Outputting something like ’Processing…’ can go a long way toward reassuring the user that their command went through. If you do print status messages like that, make sure to send them to STDERR if your utility outputs any actual data (like a report or file listing). Same for progress meters. >Conversely, don’t exit with a nonzero status code if your CLI didn’t encounter an error. Your cleverness will end up confusing and frustrating your users, especially if -e is set. Using `set -e` is the 'cleverness' here. It's a bad idea in all but a few cases. Also, i'm not sure what qualifies as 'encountering an error', but i think it's perfectly reasonable to return a non-zero status in certain non-error conditions, like when `grep` doesn't find a match, or in other cases where output is valid but empty.
- paulddraper 10y agoif grep -q foo file.txt; do echo found fi Very useful
- pyre 10y ago> i think it's perfectly reasonable to return a non-zero status in certain non-error conditions, like when `grep` doesn't find a match To be fair, grep only does that if you explicitly pass it an option that whose only purpose is to enable that behaviour.
- matt_kantor 10y agoNot true. This is grep's default behavior. From https://linux.die.net/man/1/grep https://linux.die.net/man/1/grep: > Normally, the exit status is 0 if selected lines are found and 1 otherwise. $ touch bar $ grep foo bar $ echo $? 1
- bcbrown 10y ago> return a non-zero status in certain non-error conditions, like when `grep` doesn't find a match, or in other cases where output is valid but empty This has bitten myself and several colleagues when trying to use grep with hadoop streaming. If any of the input splits doesn't have a match, grep returns an error code, and hadoop interprets that as a failure. We switch to awk in those cases.
- liw 10y agoStderr should only get error messages, and, arguably, warning messages. It shouldn't get status and progress messages. Those should go to /dev/tty, and the program should be gracefully quiet if run without a terminal, e.g., from cron.
- restalis 10y agoShouldn't the "verbose" option take care of the presence of the status or progress messages? Also, arguably, the error and warning messages are status messages too, so I would say all the status messages, regardless of gravity, belong to the same channel.
- liw 10y agoError and warning messages are not status or progress messages, at least not in the context of this discussions, which is that a long-running computation should indicate to the user that it's doing something.
- restalis 10y agoExample: "warning, the x is in y condition and thus z will take place affecting the computation in progress" or "error, could not perform x (out of many x-like things to do, but I'll continue because you instructed me to)". These are status messages in a long-running computation context.
- liw 10y agoI disagree. They're a warning and an error message, and should go to stderr. It may _also_ be useful to show them on the terminal, in case the user would like to see them now, and has redirected stderr somewhere other than the terminal. In that case a program might show them on /dev/tty as well.
- iddogino 10y agoReminds me of this super useful thread from SO http://programmers.stackexchange.com/questions/307467/what-are-good-habits-for-designing-command-line-arguments http://programmers.stackexchange.com/questions/307467/what-a...
- chriswarbo 10y agoI find myself using environment variables much more than commandline arguments when writing CLI programs: - Env vars provide a key/value interface, which is trickier to handle with arguments (e.g. "get the argument immediately after '-f'"). - Env vars are in-scope at the point they're needed, whereas non-trivial arguments usually have to be parsed up-front all at once and passed into the processing code somehow. Yes, it's good to check things up-front; we can check mandatory/mutually-exclusive env vars up front too, with the bonus that we can keep such 'invocation checking' separated from the rest of the processing. - Setting env vars in preparation for a subprocess call can be done incrementally, by extending the environment over and over until we're ready. In contrast, commandline arguments must be accumulated into a single (correctly parsable) array. - Sharing env vars across a program and its children is easy (e.g. "VERBOSE=1 ./foo.sh"). - We can use env vars to control a program even when it's called deep in the bowels of something else, without having to fiddle with all of the layers in between. I find this works very nicely, as long as we: - Treat the environment as append-only, i.e. no changing a variable once it's been set. We can append new variables, e.g. "export FOO=bar", and we can invoke processes with extra variables, e.g. "FOO=bar ./baz.sh", but we don't unset or alter a variable. - Don't use env vars for communication within a process, i.e. we can use the environment variables we're invoked with, and we can append new variables for use by the programs we invoke, but within a program we should refrain from setting variables which affect our own execution (since that causes the coupling and spooky-action-at-a-distance which globals are notorious for). What are others' thoughts on this?
- vram22 10y agoInteresting. I'll have to parse (heh) your comment, but for now, can say that IIRC, environment variables, command line options, and one other thing (maybe config or .*rc - for Run Command - files, such as .exrc, .vimrc, .bashrc) are supposed to form a hierarchy, such that A can override B can override C (something like global and local CSS styles). And out of two of them at least, command-line options can override the values of environment variables, maybe since they can be changed more easily on a per-command-invocation basis.
- dllthomas 10y agoI think if we make too much of a habit of this, we'll wind up with painful collisions. It also does the wrong thing with no feedback if you try to set a shell variable that isn't actually recognized (and possibly if you misspell the option name, &c). That said, I agree that it can work quite nicely in the small, but it should probably be reserved for things like VERBOSE where you are likely to want propagation. > Env vars provide a key/value interface, which is trickier to handle with arguments Many (most? all?) languages practical for assembling a shell utility have a getopt-ish library that should make this easier (though still likely not as easy as grabbing from a shell var, granted).
- dllthomas 10y agoI'd add "if you're going to output locations (within files), format it conventionally - a line starting with "filename:line: " or "filename:line:column: ". Like `grep -n` or most compile errors.
- MereInterest 10y agoMost, I agree with, others, I don't. > 1. Every option that can have a default option should have a default option. The exception to this are output files. A program should only output to a path that has been explicitly given to it. Too many scripts litter their working directory with output files. > 8. Don't go for a long period without output to the user. If there nothing's wrong, then nothing needs to be printed. Especially once a script gets called from another script, I don't want to have any output from a script unless it requires my attention.
- dsr_ 10y agoDefault output files: that's what stdout is for.
- dllthomas 10y ago> I don't want to have any output from a script unless it requires my attention. I lean this way, too. That said, a rare "this may take a while" before things that may take a while might do more good than harm. Another option is letting the user ask for a status update (maybe with a signal, like dd?).
- userbinator 10y agoMost, I agree with, others, I don't. This is the fundamental reason why I've grown tiresome of the phrase "best practices". They probably are the best you can come up with, but that doesn't mean they're the best (or sometimes even close) for everyone else.
- MereInterest 10y agoI think the discussions are useful, even if the conclusions aren't. Discussing best practices gets everyone to bring up the pros and cons of each. Then, when confronted with a new area, one can apply those arguments to figure out a decent set of best practices for that particular field.
- vkjv 10y agoThis is missing something that I highly recommend: "Start with supporting stdin/stdout as the only input and output. This ensures that it is composable with other utilities. You may find you never need anything else" Need to read a file? `my-cli < file` Need to write a file? `my-cli > file` How about read from a URL? `curl url | my-cli`
- jpitz 10y agoWhile I agree that v0.1 should support stdin/stdout, I don't know that you are serving all of your users well by limiting i/o to ONLY stdin/out.
- lucb1e 10y agoThe two applications for specifying files on the command line is when: they actually do something with that file (e.g. move the file) or when you operate on multiple files and/or directories (e.g. backup application). Otherwise it still might be useful, but in general it's kind of unnecessary. It adds logic to your application that it doesn't need, which violates "do one thing, and one thing very well" (albeit only a very small violation).
- jpitz 10y agoISTM that you're arguing from a perspective of intrinsic necessity. Your argument is that, anyone can cat a file into my utility, if they need that functionality. Sure. However, for example, GNU sort doesn't work that way. Most utilities don't work that way. Most utilities accept a file as an source of input, and most of those don't act on the inode. That's the status quo.
- marvy 10y agosort is a bad example: to support sorting in-place, it has to know the file name. Pity the user who typed sort < bigfile > bigfile for said user just lost a file.
- fredrb 10y agoVery good article. Another point that I think is valuable is: "Output one line per record data" This is useful if your CLI that has to output data to the user. Makes it easier to interface with programs like `grep`
- gizmo686 10y agoI'd add to that an option to deliminate records with NUL instead of new line and treat all other characters as literals.
- shabble 10y agoSuppressing the header line and any fancy formatting jazz is a nice idea as well, since otherwise there's invariably some buggery needed to strip it like: ps | tail -n +2 | cmd_with_just_records or, if you're doing column extraction as well, I like: ps | perl -lanE'say $F[0] if 2..eof' [ps used here as a stand-in for some other command that has a fixed header. I'm aware that you can omit it via hte somewhat long-winded `ps -o "pid= command= [...]"' there, but afaik there's no simple switch for 'do what you were going to, just without a header line at the top']
- lucb1e 10y agoGood and useful advice. Just one minor thing I'd change (copied from a comment that I posted there too): Don't use -v for version. It's very uncommon (though I know software that does it, it's confusing) and people will often blindly add -v for verbosity. A better alternative is -V (capitalized) and of course --version. These should both work, just like -h and --help should both work.
- belovedeagle 10y agoAlso, this fits in with advice #3, use common options. Users aren't "blindly" adding -v for verbosity; they're expecting it as a common option. Admittedly, the verbose/version/something else distinction is not as standard as I'd like, but if I were writing this I would have explicitly said in point 3, "-v is verbose. -V is version. -h is help. -?, if implemented at all, is a synonym for --help. -n is dry run, if that makes sense for your command. (By the way, unknown flags should cause the command to fail rather than silently do the wrong thing.) The first instance of -- causes all further arguments to be blindly accepted as positional arguments rather than flags, including further uses of --. If at all possible, all command forwarding (e.g., ssh, shell, autocall) is done using multiple positional params, not nested quoting nightmares."
- lucb1e 10y agoAll agreed. Most importantly, unknown arguments should not go by silently and -- should be implemented.
- barbs 10y agoI find it quite annoying every time I need to check the installed version of java and `java --version` doesn't work (I need to type `java -version`).
- lucb1e 10y agoI've made that mistake at least once, if not more than once.
- 10y ago
- jakub_g 10y agoSemi-related: if you write CLI tools with nodejs, I highly recommend using yargs and/or inquirer - yargs for robust command line arguments parsing, and inquirer for interactively asking questions to the user. https://github.com/yargs/yargs https://github.com/yargs/yargs https://github.com/SBoudrias/Inquirer.js/ https://github.com/SBoudrias/Inquirer.js/ When using these tools correctly (following readme), you get most of the stuff written in the article out of the box.
- paulddraper 10y agoEw. Most programs shouldn't ask me questions. They should print a usage statement if I haven't provided enough info.
- flukus 10y agoYep, they're commands, not conversations.
- sotojuan 10y agoAnd if you want something a bit above yargs (and to prove the point of JS having many solutions to everything!) use `meow`: https://github.com/sindresorhus/meow https://github.com/sindresorhus/meow
- justsee 10y agocli-kit is rather comprehensive, well-thought out, and modular too if you're looking for Node options: https://github.com/cli-kit/cli-toolkit https://github.com/cli-kit/cli-toolkit
- andrewstuart2 10y ago> 12. Write to stdout for useful information, stderr for warnings and errors. I would constrain this even further to "Write to stdout only if information is useful as input to another program." Even useful information for a human reader can make downstream integration overbearingly complex if the information is intermingled with a lot of extraneous, albeit human-readable, information. Machine-readable layouts (structured somehow: csv, tsv, json, xml, etc.) are vastly more useful for integration.
- niftich 10y agoI strongly disagree. A CLI is fundamentally for the benefit of humans interacting with the application, and not for the benefit of interprocess communication. When run interactively, a CLI should print human-useful information on the console. Conversely, a structured way of exposing program outputs and state should be the preferred way of interprocess communication. Because of convention and deliberate design choices, on the Unix command line, these two often find themselves in conflict. In my opinion, the solution isn't to compromise human usability to support machine-consumability of outputs.
- new_hackers 10y agoDisagree. A CLI is fundamentally for executing from the command line. Thus, embracing the power of the command shell is best practice. If you want a human to see it, put it on stderr. If you want a machine to see it, put it on stdout. Also I strongly agree with making it "structured" output. At minimum, a line-oriented record output is fairly easy to process downstream. EDIT: If it is an interactive console application (REPL), then stdout is okay i guess.
- smhenderson 10y agoWould a good compromise be to provide -q|--quiet to suppress extra, human readable messages when piping out to a different app?
- 10y ago
- slantview 10y agoI used to use Ruby for CLIs. Then I started using Golang and delivering compiled binaries for each OS that I supported. This has been a game changer for me. With the CLIs that I've worked on that are open source https://github.com/RiotGamesMinions/motherbrain https://github.com/RiotGamesMinions/motherbrain and some other ones in Ruby, we consistently had issues with rubygems and having people be able to run our CLI from version to version. Using something like Golang for distributing compiled binaries means that as long as they have the CLI, it will continue to work. With Ruby, Python, PHP, etc, there is absolutely no guarantee that your application will work in the future.
- yannis 10y agoGo has been a game changer for me also. There is also a very good package https://github.com/spf13/cobra https://github.com/spf13/cobra that can make the development of complex CLIs easier.
- aikah 10y agoGo default flag package is pure garbage though. That's a fact. However, Go cross compilation makes it easy to develop multi-os CLI tools. The downside is the size of the executable, it's easy to reach the 50MB bar with Go binaries.
- skj 10y agoYes. The default "flag" package is trash. I like the Go team a lot, and I think they did a great job with many things, but it seems to me like they just said, "meh, get something that can work, usability is not something that matters."
- ptman 10y agoconsider the history, it probably makes sense if you're used to research unix and plan9
- integrii 10y ago
- tomgagnier 10y agoProviding command line completion should be on this list!
- Retra 10y agoHow would you do that unless you were changing the CLI itself?
- deathanatos 10y agoModern shells, such as bash or zsh, support specifying completion through external files or scripts that the shell can parse. Having never written one, I'm not familiar with the exacts, but suffice it to say the right file in the right location with the right contents can inform the shell as to how to auto complete. i.e., the auto-completion facilities are general / extensible. Especially if many programs follow a general format of program subcommand arg arg arg --optional-flag --option (my personal favorite, as I find it most clear; followed by e.g., argparse in Python, git, many GNU utilities) then it should be easy to see how a small specification of what subcommands take what for args or options should be enough to enable a pretty powerful auto-complete. (This is an example; I think zsh's autocompleters are actually small scripts; see https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org https://github.com/zsh-users/zsh-completions/blob/master/zsh...) (This, in zsh, combined with zsh's fuzzy autocomplete, is amazing.)
- dllthomas 10y agoFor bash: $ help complete complete: complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...] Specify how arguments are to be completed by Readline. For each NAME, specify how arguments are to be completed. If no options are supplied, existing completion specifications are printed in a way that allows them to be reused as input. Options: -p print existing completion specifications in a reusable format -r remove a completion specification for each NAME, or, if no NAMEs are supplied, all completion specifications -D apply the completions and actions as the default for commands without any specific completion defined -E apply the completions and actions to "empty" commands -- completion attempted on a blank line When completion is attempted, the actions are applied in the order the uppercase-letter options are listed above. The -D option takes precedence over -E. Exit Status: Returns success unless an invalid option is supplied or an error occurs. There are a lot of canned completion approaches, usually tweakable in small ways, but for heavy lifting `complete -F foo bar` will call the function `foo` when you are asking for completion of a command where the first word is `bar`. Information on what words are already on the line, where the cursor is, etc is passed in in shell variables starting with COMP_. See the "Programmable Completion" section of the bash manpage for much more detail. Note that it's settings for a specific shell process, not global across all instances of bash. "The right file in the right location" is only relevant 1) to set up your shells a particular way by default, and 2) if the function called references a file.
- vram22 10y agoAlso, -- (two dashes) in the command line means "end of the options", which allows, e.g. rm to delete a file starting with dash.
- TheAceOfHearts 10y agoRelated to CLI best practices, is there any good styleguide or "best practices" list for formatting help and text output? I've only written a handful of small CLI utils, and each time I end up looking through tools I regularly use and adapt ideas from each. But this is more time consuming than being able to run through a list of suggestions prepared by someone with more experience in this domain.
- okdana 10y agoPOSIX talks about it a little bit: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_... Some argument-handling libraries like Python's argparse and PHP's Symfony Console generate usage help for you, and they all use a relatively similar formatting that i think was inspired by the formatting of man pages: http://man7.org/linux/man-pages/man7/man-pages.7.html http://man7.org/linux/man-pages/man7/man-pages.7.html docopt (http://docopt.org/ http://docopt.org/) is an attempt to standardise it from the other way around — instead of you defining the options in the code and it generating the usage help for you, you write the usage help and it generates the code to define the options. But i don't know that it's super widely used.
- dredmorbius 10y agohttps://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html https://www.gnu.org/prep/standards/html_node/Command_002dLin... https://www.freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/policies.html https://www.freebsd.org/doc/en_US.ISO8859-1/books/developers...
- rlpb 10y ago> 5. Don’t have positional options. But also, don't conflate options with arguments. Options should be optional (the clue is in the name) and prefixed with - (single letter options) or -- (long form options). Arguments are typically positional, but as the author says, they can become confusing if there are too many, or the ordering is non-obvious, in which case the syntax may need a rethink. A blanket "don't use positional arguments" would be tedious though. For example, if I ignore what I say above, imagine having to type "cd --directory=foo" or "cp --source=foo --destination=bar" all the time.
- dllthomas 10y ago> "cp --source=foo --destination=bar" Cf. `dd if=foo of=bar` Not really disagreeing, just adding context.
- slrz 10y agoThen maybe one should also add the bit of context that dd's command line syntax was intended as a joke, a pun on some job control language of yore.
- dllthomas 10y agoOh, really? Interesting. While... odd, I always thought it appropriate to be extra explicit with dd, where for some common uses you've the chance of nuking the wrong disk. Do you know which job control language of yore?
- slrz 10y agoOS/360 JCL, apparently. At least Rob Pike wrote something to the effect on his Google+. dd is horrible on purpose. It's a joke about OS/360 JCL. But today it's an internationally standardized joke. I guess that says it all. How the hell does one direct-link to a G+ comment? Well, cllck the thing that says "View 384 bazillion previous comments" and then search for dd. https://plus.google.com/+RobPikeTheHuman/posts/R58WgWwN9jp https://plus.google.com/+RobPikeTheHuman/posts/R58WgWwN9jp
- endgame 10y agoAlso: Write a manpage.
- ryanlm 10y agoI'm hoping the angular cli will be able to send email soon.
- beefsack 10y agoOne trend I've really not appreciated in recent years is neglecting man pages for command line utilities. Having a `--help` option is great as a cheat sheet, but nowadays these help outputs are much too large and hard to navigate because. Alongside complete man pages, help commands can be helpful high level overviews focusing on common use cases with examples.
- ams6110 10y agoThanks for saying this, I was about to say the same thing. Write a man page. --help or -? is OK for a terse summary but don't put detailed instructions in --help output, because then I have to run it twice (since I didn't add '| less' the first time).
- dredmorbius 10y agoThat's been a long-standing beef of mine for well over a decade. There are several apparent sources of this. The GNU project deprecates man in favour of info. This is a category error for numerous reasons. The good news is that the documentation still exists, man can be configured to fall back to 'pinfo' or similar, and projects (Debian are particularly good about this) can schlep the Info format into a manpage. There's an argument in favour of info: it was a hypertext document format which is, arguably, more powerful than man. On the other hand, it has a format-specific reader (info), there's a far more dominant hypertext document format (HTML), and there are utilities to provide manpages in HTML format and over a local HTML server (dwww:https://packages.debian.org/stable/doc/dwww https://packages.debian.org/stable/doc/dwww). GNU should have bailed on this decades ago. Several projects, notably GNOME (a GNU project) and many Red Hat utilities, lack good / updated / any manpages. This is particularly frustrating. KDE similarly fails frequently to provide manpages. Again, Debian frequently will create and provide manpages, but they will frequently run behind package development, so new features aren't clearly documented. Increasingly, standalone packages (say, imagemagick) don't fully document themselves in manpages. Worse are vendor utilities which lack any manpage, a useful --help or -? -h page, or anything else vaguely resembling sane practices. This is entirely inexcuseable.
- restalis 10y agoMaybe including dependent switches to enable/disable different parts of shown information? For example just "--help" would show a short description and the existing switches for further debriefing. Then you'll have to type "--help --commands" for showing a list of commands and description in one or two words, or "--help --examples" to show a whole lot of examples, and so on.
- ams6110 10y agoThis was all figured out in the 1980s.
- vacri 10y agoNew techies are coming online all the time.
- dredmorbius 10y agoOne advantage the teaching profession has is that new opportunities are being created daily.
- sigil 10y agoGreat advice on the whole! > 3. Use common command line options. The GNU options standards are good, but see also the "Command-Line Options" chapter in "The Art Of Unix Programming" for some of the reasoning and history behind these conventions. Actually -- if you're writing unix programs often, do yourself a favor and read all of TAOUP. It really helps crystallize the Unix Philosophy in a way that a grab bag of suggestions doesn't. http://www.catb.org/esr/writings/taoup/html/ch10s05.html http://www.catb.org/esr/writings/taoup/html/ch10s05.html > 4. Provide options for explicitly identifying the files to process. Lots of options pointing to different files is a CLI smell. If your program "does one thing and does it well" (the Unix Philosophy), it often works like a filter: take one or more input files, apply the same transformation to them, output the result. `cat`, `head`, `tail`, `grep`, `cut`, `sort`, `join`, `sed`, `gzip`, `tar`, `cc`, `curl`, `md5sum` -- at the heart of all these programs is a unix filter. In this case, just have your program accept input files as positional arguments beyond the last option. A nice side effect is that your program will be `find | xargs` -friendly. That puts the user in total control of the inputs. It also means they can parallelize your program with `xargs -P`! Also consider whether you need input files at all! Can your unix filter program just transform stdin to stdout? If so, it can be used to process streams of data much larger than available disk space. Often you can trick programs that require a file argument by passing `/dev/stdin`, but note that `/dev/stdin` won't be accessible in some environments. > 8. Don't go for a long period without output to the user... Outputting something like ’Processing…’ can go a long way toward reassuring the user that their command went through. I get the reasoning behind this, but as a heavy CLI user I'd prefer if programs didn't crap progress all over the terminal by default. If I want verbose progress, I'll pass `-v`. If I want even more insight into what's happening, I'll pass `-v -v`. See Rule Number 11 in TAOUP: Rule of Silence: When a program has nothing surprising to say, it should say nothing. http://www.catb.org/esr/writings/taoup/html/ch01s06.html http://www.catb.org/esr/writings/taoup/html/ch01s06.html > 10. For long running operations, allow the user to recover at a failure point if possible. One way to perform a sequence of expensive steps iff they haven't been done yet ("recover at failure points"): orchestrate it with a Makefile.
- vram22 10y ago>> 8. Don't go for a long period without output to the user... Outputting something like ’Processing…’ can go a long way toward reassuring the user that their command went through. >I get the reasoning behind this, but as a heavy CLI user I'd prefer if programs didn't crap progress all over the terminal by default. If I want verbose progress, I'll pass `-v`. If I want even more insight into what's happening, I'll pass `-v -v`. >See Rule Number 11 in TAOUP: >Rule of Silence: When a program has nothing surprising to say, it should say nothing. Agreed. In fact, informally, that rule goes back to much before TAOUP was written, I think. (TAOUP may have only formalized it.) I remember something like it from classic The Unix Programming Environment book (UPE, I first read it several years ago) which I referred to in another comment in this thread. IIRC, in UPE it may have been phrased a bit differently, that's all - something like: if the program succeeds (in some cases, don't generate any output, at least on stderr - of course if the program generates output as part of its normal behavior, like filters and some other programs do, then it has to write to stdout even if no errors). The behavior of the cmp command (compare two files) is an example of that - it produces no output on a successful compare (the files match), only on an unsuccessful one (the files differ). Also, part of the reason for the brevity of Unix commands and terseness of output, is supposed to be because the first Unix versions were actually developed on teletypes (which were like the old telex machines) for output - that actually printed the output as you typed commands, on rolls of paper. Video screens came later. And the same is the reason for the Unix editor ed's brevity, and even it's p(rint) command - you actually had to (physically) print the changed lines after an edit, to even see them ... :-) All in all, I'd say it was an even more incredible job to develop an OS like Unix under such constraints. There is also pipe viewer. Peteris Krumins wrote a post about it a while ago: http://www.catonmat.net/blog/unix-utilities-pipe-viewer/ http://www.catonmat.net/blog/unix-utilities-pipe-viewer/ You insert it at points in the pipeline and it lets you see the progress of the pipe. And totally agree with your recommendation of TAOUP. The wording can be a little ornate / verbose at times, but that is just ESR's style. Worth tolerating it for the content.
- rossy 10y agoI'd expand on this and say every command line program should support GNU getopt_long() syntax, since this is the most popular syntax for command line interfaces and it saves having to guess the syntax when using a program for the first time. This doesn't mean all programs should use getopt_long(), but they should understand all the syntax that getopt_long() understands, such that someone could reimplement the program's command line interface using getopt_long() and get exactly the same behaviour. This means, for example, supporting both space-separated and equals-separated option values (--name value and --name=value). Some programs only support one or the other, so you have to guess which will be supported when invoking a program for the first time. Both forms have their uses too. --name=value is more explicit and should throw an error if the --name option doesn't take a value, so it's good for use in scripts. On the other hand, a lot of shells don't tab-complete file names unless the file name is in a separate argument, so for interactive use, --name value can be more convenient.
- d0mine 10y agohttp://docopt.org/ http://docopt.org/ demonstrates the common convention for CLI interfaces.
- Annatar 10y agoI'd expand on this and say every command line program should support GNU getopt_long() syntax, since this is the most popular syntax for command line interfaces and it saves having to guess the syntax when using a program for the first time. One of the greatest crimes which GNU is not UNIX has committed are the --long-options, and that horrible practice stems from the fact that command line applications on GNU/Linux have poorly designed usage displays, and even worse (often non-existent) manual pages, and when there is a manual page, it lacks an EXAMPLES section, in stark contrast to UNIX systems (Solaris, HP-UX and SmartOS are exemplary). Ease of UNIX and efficiency thereof comes from brevity, and that particularly concerns command line options: the lowest amount of typing possible is the goal. The solution is not to invent an arbitrary and horrible convention, but to design better usage output, and to deliver high quality manual pages with lots of examples. Treat the root cause, not the symptom.
- 10y ago
- vacri 10y agoFor in-house stuff, I also run by the rule that any tool should be safe to run if there are no args provided. Run any of my tools, and if it's potentially destructive, you'll only get a help screen if there are no args provided. Even tools that don't need any args to function will still require a misc arg if they might theoretically damage something. The theory here is that these tools aren't well-known, so it's an extra level of safety. It also means that colleagues don't have to hunt me down to ask about what it does...
- emmelaich 10y agoMy favourite annoyance is using the option syntax for things that are not optional. If it really is a command, then require a command without the -- or -. Today's examples: 1. centos6/redhat6 /sbin/chkconfig; it needs one of --list or --del or ... so why not just list or del or ? 2. the kafka command line tools, e.g. kafka-topics. It requires --create or --delete or --list ... So just use create/delete/list/... !
- restalis 10y ago"it needs one of --list or --del or ... so why not just list or del or ?" That's all good and everything but what about the parameters for which optional is only the explicit digression from an existing default value?
- inlined 10y agoGood feedback. I have one comment and one question: 1. I personally prefer to present a terse message to stderr and stack traces to a debug file. This helps a user give support more information when they're hitting transient issues. 2. I fully agree with the recommendation for a --dry_run option, but I think the output needs to be actionable. Do people have good examples of actionable --dry_run output that lets the user verify their intent? E.g. The list of files that would be deleted by "rm -rf"
- newman314 10y ago+1 to Option #1 I'd argue that good software has what I call "good factory defaults". Pine was a good example of this. It allowed people to use email right out of the box with plenty of options for customization in it's config settings.
- dredmorbius 10y agoNo idea why the hivemind is rejecting this, but yes, absolutely. Defaults should be sane, non-destructive, intuitive, and suit the common case. People don't change defaults, and actions which can be destructive should not be easy to invoke accidentally. Even (or especially) on a CLI.
- dredmorbius 10y agoAs is typically the case in software, different standards tend to emerge from different projects and their associated tools. Two of the primary diverging Linux/Unix standards are FreeBSD (arguably the older, from AT&T & BSD traditions) and GNU. The GNU programming standards specifies standards for command line interfaces in section 4.7, strongly influenced by GNU getopt(3). https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html https://www.gnu.org/prep/standards/html_node/Command_002dLin... This references the Table of Long Options (this needs to be worked in to Game of Thrones): https://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table https://www.gnu.org/prep/standards/html_node/Option-Table.ht... For FreeBSD, the equivalent guide appears to be (I'm not an expert at this, so salt appropriately) the kernel source file style guide: https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9&manpath=freebsd-release-ports https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9&ma... There may be another source. I've seen other significant projects impose their own argument styles. Among those: MIT/X11 has its own family of arguments. Various toolkits seem to engender their own styles. The GNOME and KDE projects have tended to both adopt idiosyncratic and largely undocumented argument formats. I find both tremendously frustrating. Major applications often develop yet more idiosyncratic command and argument syntax. Chrome, Firefox, and LibreOffice come to mind. I'm going to pretend Java doesn't exist at all. Nope. It's a myth. The notorious 'dd' owes its syntax to mainframe JCL notation, from which it is derived.
- ozten 10y agoGreat post. An amazing opt library is docopt[1]. Instead of writing a lot of code, with docopt you write your usage doc based on long standing best practices and docopt parses that USAGE block. [1] http://docopt.org/ http://docopt.org/