[HN Gopher] "Rules" that terminal programs follow
       ___________________________________________________________________
        
       "Rules" that terminal programs follow
        
       Author : charlieok
       Score  : 245 points
       Date   : 2024-12-12 17:09 UTC (1 days ago)
        
 (HTM) web link (jvns.ca)
 (TXT) w3m dump (jvns.ca)
        
       | matheusmoreira wrote:
       | > programs should print "regular" output to stdout and errors to
       | stderr
       | 
       | This is really important. I'd like to expand on this.
       | 
       | Standard output is for the data the program was asked to produce,
       | no more and no less. If user asked for some JSON data, standard
       | output should contain that exact JSON object and absolutely
       | nothing else.
       | 
       | Standard "error" is actually a misnomer. It should have been
       | called the standard user stream. Anything meant for the user to
       | read on the terminal is supposed to go there. Error messages are
       | of course included in that set but so are status messages and
       | verbose output.
       | 
       | This ensures the output of programs can be piped into other
       | programs seamlessly. Non-output data still gets sent to the
       | terminal or redirected somewhere else.
       | 
       | Would have been great if programs were able to easily create new
       | terminal-connected file descriptors for specific purposes. They
       | could document those numbers in their manuals just like they
       | document exit codes. Then users would get "ports" for every
       | output. Could cut down on parsing significantly.
       | 
       | For compatibility, they could all redirect to either standard
       | output or standard error by default... I think I'm gonna
       | experiment with this a bit.
        
         | packetlost wrote:
         | I'm not sure I like the "standard user stream" name, but I
         | otherewise agree with everything here.
        
           | matheusmoreira wrote:
           | Yeah, naming is hard. If there is a better word for "non-
           | output", I don't know it.
        
             | packetlost wrote:
             | I would maybe call it stdlog or stdinfo vs stderr, but yeah
             | naming things is hard.
        
               | matheusmoreira wrote:
               | I think stdinfo would exclude error messages from the
               | definition, just like stderr excludes info messages.
               | 
               | I really like stdlog. Standard log stream is a pretty
               | awesome name. Short and terse, the word "log" doesn't
               | even need abbreviation and it's correct since it's a
               | superset of error and info streams and also generic
               | enough to cover other unforeseen categories.
               | 
               | I'll use it from now on!
        
               | packetlost wrote:
               | The idea for info is that it's informational, which is
               | always the case regardless of whether it's printing error
               | messages due to a failure or informational messages
               | because the user specified a `-v` flag or whatever.
               | Whether the command failed or not is communicated via the
               | exit code. That being said, I do prefer stdlog for the
               | reasons: it communicates the intent and usage as
               | unambiguously and tersely as possible.
        
               | matheusmoreira wrote:
               | > because the user specified a `-v` flag or whatever
               | 
               | The way I see it is the version information and help text
               | belong on standard output if the user passes --version
               | and --help since that's what the user explicitly asked
               | for. When the command is invoked incorrectly, the help
               | text should go to standard error while standard output
               | should be empty.
               | 
               | I agree about the exit code. People like to parse error
               | messages and that's always wrong. Everything should be
               | done via exit codes instead.
        
               | packetlost wrote:
               | The `-v` was in reference to the common convention of one
               | or more `v` flags increasing the verbosity of logging.
        
               | ryandrake wrote:
               | C++ has std::clog[1], but it's basically cerr, and the OS
               | still only knows about stdin, stdout, and stderr.
               | 
               | 1: https://cplusplus.com/reference/iostream/clog/
        
           | paulddraper wrote:
           | I'd name it "standard log."
           | 
           | That's what it is....a log. Of errors, warnings,
           | informational messages, whatever to complement the primary
           | output.
           | 
           | BONUS: stdlog fits nicely :)
        
           | saghm wrote:
           | What about "debug"? I think the important part about stderr
           | is that it's "meta" info about how the program is running (or
           | ran) rather than what the program is intended to produce as
           | output. This seems pretty similar to what often is referred
           | to as the "debug" level of logging (which includes lower
           | levels of logging like warnings and errors).
        
         | lionkor wrote:
         | Unless your app doesn't generate output,then grepping the
         | output will not work if it's stderr and it will be confusing
        
           | bombcar wrote:
           | You can learn the 2>&1 thing, but it would be nice if there
           | was a feature of one of the greps to "slurp up" the error
           | output.
           | 
           | Of course, the real problem is there's no standard, the
           | standards that do exist are ignored, and each new command-
           | line tool generates a new standard.
        
             | sudobash1 wrote:
             | It generally isn't possible for grep to "slurp up" the
             | error output because stderr does not get passed through
             | pipes (by default). This is shell behavior and grep cannot
             | do anything about it.
             | 
             | As a side note, some shells have started implementing a
             | shorthand for `foo 2>&1 | grep` which is `foo |& grep`.
        
               | ElevenLathe wrote:
               | Probably you could do a hack like finding the program at
               | the other end of the pipe and looking at its open file
               | descriptors, but lots of corner cases to consider.
        
               | kelnos wrote:
               | Right, the problem is that apparently shells haven't seen
               | user need for selectively redirecting one or the other.
               | Like I might want to see foo's stdout on the terminal,
               | but pipe stderr (and _only_ stderr) to grep, perhaps so I
               | only see some specific error messages that I care about.
               | `2 >&1` will send stderr to stdout, and then the pipe
               | will send _everything_ to grep (and I believe `| &` does
               | the same).
               | 
               | (To be fair, that _does_ seem like a pretty rare use
               | case.)
        
               | sudobash1 wrote:
               | Actually, you can. If you run:                   foo 2>&1
               | >&- | grep ...
               | 
               | then you will grep only the stderr. The stdout will be
               | discarded.
               | 
               | Alternatively, if you only want to grep to see the
               | stderr, but you still want to see stdout, you can swap
               | stderr and stdout like this:                   foo 3>&2
               | 2>&1 1>&3 | grep ...
               | 
               | And in many shells, you can easily split the stdout and
               | stderr into two separate pipelines like this:
               | foo > >(grep stdout) 2> >(grep stderr)
        
               | wahern wrote:
               | > Like I might want to see foo's stdout on the terminal,
               | but pipe stderr (and only stderr) to grep, perhaps so I
               | only see some specific error messages that I care about
               | 
               | You mean this:
               | 
               | `foo 3>&1 1>&2 2>&3`
               | 
               | The above swaps stdout and stderr, saving + restoring the
               | original stdout through descriptor 3. Shell redirection
               | expressions map directly to dup2 syscalls, except the
               | operands are reversed: 3>&1 is dup2(1, 3). Pipes map
               | fairly simply to fork + exec. At its core the shell is a
               | rather thin wrapper around the core Unix syscalls fork,
               | exec, dup2, open, and close. If you look at the original
               | shell implementation, the command parser more-or-less
               | executes these syscalls as it goes along, left to right.
        
           | matheusmoreira wrote:
           | How can you grep output if there's no output?
           | 
           | Can you provide a concrete example of such an application?
        
             | d3VwsX wrote:
             | Some applications print usage information (e.g. with --help
             | flag) to stderr. That is a bit annoying when there is a lot
             | of output and I just want to grep for some flag, since the
             | first attempt will fail and then I have to try again after
             | adding 2>&1.
        
               | quectophoton wrote:
               | Then there's `ssh-keyscan`, printing the "real" output to
               | stdout, but also printing "comments" to stderr.
               | 
               | Nothing wrong with that, but it was a bit confusing the
               | first time I saw that behavior.
        
             | quectophoton wrote:
             | I _think_ what they meant was something like:
             | 
             | > Unless your app doesn't generate [stdout] output,then
             | grepping the [visible] output will not work if it's stderr
             | and it will be confusing
             | 
             | With the first "output" meaning "specifically stdout", and
             | the second meaning "what you see on the terminal".
             | 
             | As in, you run a command, see it printed a lot of stuff, so
             | then you do it again but piping it to `grep` or `less`, and
             | realize none of that output was actually going to stdout.
        
         | AndrewDucker wrote:
         | This is definitely one of the things that PowerShell got right.
         | 6 different streams, each of which can be intercepted
         | separately and configured differently.
         | 
         | https://learn.microsoft.com/en-us/powershell/module/microsof...
        
           | matheusmoreira wrote:
           | There's a lot of good ideas in power shell. I like how they
           | broke up the log stream into separate streams by severity. It
           | still seems arbitrary and insufficiently general though. Why
           | severity and not some other criteria?
           | 
           | What if programs could define any number of output streams?
           | By default they could all coalesce into the terminal but
           | other programs could connect to each one separately if they
           | needed. Like an audio mixer of sorts: by default you get the
           | mixed audio but there are ways to access each individual
           | voice if needed.
        
           | immibis wrote:
           | I don't think that 6 different streams that will always go to
           | the same place but some of them are usually turned off are
           | very useful. Standard-error/user is useful because it goes to
           | a different place. Levels would be better suited to a simple
           | global variable, or a level associated with each printed
           | line. The chance that someone redirects errors to error.log
           | is reasonable, but the chance that someone redirects errors
           | to error.log _and_ warnings to warning.log _and_ debug to
           | debug.log _and_ verbose to verbose.log is pretty low.
        
             | AndrewDucker wrote:
             | I turn on Verbose logging sometimes, to get more info.
             | 
             | Error logging can be set to either spit out the error and
             | continue, or to stop processing, which is very useful.
             | 
             | Warning and error messages come out in their own colours
             | automatically, which is nice.
             | 
             | And sure, you can just have them all come out to the same
             | place. But having the options to configure them is very
             | handy.
        
               | immibis wrote:
               | None of this is related to them being separate streams
               | instead of metadata in one stream
        
           | MatejKafka wrote:
           | Also related to the article, PowerShell has PSReadLine, which
           | implements a pretty reasonable text editor, including
           | selection, copy/paste, classic keybindings such as
           | Home/End/Ctrl-arrows, sane multiline command editing,
           | semantic autocomplete and custom actions that can operate on
           | input AST.
           | 
           | Once you get used to having all that, going back to other
           | shells is pretty hard.
        
       | bobbiechen wrote:
       | Nice writeup. Since she mentioned how hard it is to learn these
       | conventions, I'll plug my preferred reference when thinking about
       | CLIs specifically (rather than TUIs and REPLs) - the Command Line
       | Interface Guidelines https://clig.dev
       | 
       | It does include the blog post's rules on exiting on Ctrl-C,
       | accepting `-` for stdin, disabling color in pipes, and much more.
        
         | thiisguy wrote:
         | This is referenced at the very beginning, just after the table
         | of contents.
        
           | bobbiechen wrote:
           | Oops, I missed it when I was reading, and assumed it wasn't
           | referenced. Thanks!
        
         | mcint wrote:
         | I would also recommend, call to attention, the Further Reading
         | from CLIG, https://clig.dev/#further-reading. POSIX, GNU, Unix
         | resources, Heroku CLI guide, and 12-factor _CLI_ app guide.
        
         | snthpy wrote:
         | Thanks. Didn't know about that one. There is also:
         | 
         | https://usage.jdx.dev/
        
       | felixhummel wrote:
       | I'd add "long-running processes should reload their configuration
       | on SIGHUP". :)
        
         | rocqua wrote:
         | This isn't a list of shoulds. It's a list of, generally dones.
         | Big difference.
         | 
         | This one is still nowhere near universal enough to count in the
         | original lisr sadly.
        
         | throwaway127482 wrote:
         | This behavior seems difficult to actually implement in
         | practice, no? Your application has to be careful not to
         | accidentally cache any configuration state, as well as any
         | state _derived_ from the configuration, otherwise it will go
         | out of sync. Maybe easy for trivial apps but most apps
         | requiring configuration files are non trivial.
        
         | abbeyj wrote:
         | This is probably technically out of scope for this article.
         | Long-running processes that respond to SIGHUP will usually be
         | running detached (with no controlling TTY). Thus it is a bit of
         | stretch to call them "terminal programs".
        
       | anthk wrote:
       | Don't hardcode readline keybindings.
        
       | Sesse__ wrote:
       | "rule 5.1: Ctrl-W should delete the last word [...] I can't think
       | of any exceptions to this other than text editors but if there
       | are I'd love to hear about them!"
       | 
       | mysql(1) only links to editline instead of readline, where Ctrl-W
       | by default deletes everything to the start of the line, not just
       | the last word. It drove me mad in the period where I had to use
       | it; you just see your entire nice query disappear. :-)
        
         | saghm wrote:
         | It could be worse! Back in college, I had the misfortune to
         | decide to try to use sqlplus, Oracle's CLI to try to connect to
         | their database. Not only did terminal shortcuts like Ctrl-W not
         | work at all, but you couldn't even move the cursor earlier in
         | the line, and there was no history navigation to get to the
         | previous command, so any typo forced you to retype the entire
         | thing from scratch. Entering a single forward slash would allow
         | you to run the last command verbatim, but that didn't help with
         | typos. Ctrl-L also didn't work for clearing the screen, but you
         | could manually run `clear scr` to do it instead, because I
         | guess having the command just be `clear` wouldn't be obvious
         | enough.
        
           | cafard wrote:
           | SQL*Plus is pretty basic, but L will show you the previous
           | command, a number will give you that line of the previous
           | command, to edit, etc.
        
           | Sesse__ wrote:
           | If you ever need to do so again, try gqlplus, which wraps
           | sqlplus to add such quality-of-life features :-)
        
             | saghm wrote:
             | I hope my streak of almost a decade of not needing to use
             | Oracle will continue, but if not, I definitely will be
             | looking for alternatives to sqlplus...
        
           | kps wrote:
           | It could be worse! You could be using an un-configurable
           | modern browser or some such thing where attempting to erase a
           | word closes your tab.
        
           | emmelaich wrote:
           | Pretty sure Oracle's SQLplus supported the `e` command for a
           | long time. Which invokes $EDITOR on last command.
           | 
           | On Unixes of course, don't know what VMS/MVS/.... did.
        
             | saghm wrote:
             | Interesting! Too bad my googling didn't come up with that,
             | since it was not at all easy to discover anything from
             | within the shell.
        
       | CorrectHorseBat wrote:
       | ctrl-w and ctrl-u are just the emacs edit commands from readline.
       | If you are a sensible person and use vi mode they do nothing.
        
         | blueflow wrote:
         | Do an "stty -a" in the terminal - you will see these two keys
         | are handled by the kernel itself and will also work with
         | programs reading dumbly from stdin, like "cat".
        
         | kps wrote:
         | Ctrl-W originates (on Un*x) from the 'new' BSD tty driver in
         | the late '70s. Ctrl-U vs Ctrl-X was one of those BSD-vs-AT&T
         | things (where V7 defaulted to '@' 'cause your terminal might
         | not have those fancy control characters). `vi` respected the
         | current tty settings.
        
         | d3VwsX wrote:
         | Ctrl-w and ctrl-u in a default emacs do not even do the same
         | things as in readline.
         | 
         | C-w is (usually) kill-region (similar to what most editors call
         | "cut").
         | 
         | C-u is universal-argument, that is used as a prefix for other
         | commands to add arguments.
        
       | sysread wrote:
       | Non-interactive programs that emit informational output should
       | _only_ do so to stderr or a log file so that they may be used in
       | a pipe line.
        
       | model-15-DAV wrote:
       | As an addendum to Rule 7, `cd -` takes you to the last opened
       | directory. Or is `cd` considered part of the terminal emulator's
       | job, as a built-in?
        
         | saghm wrote:
         | I don't think it's part of the terminal emulator (e.g. xterm,
         | gnome-terminal) but the shell (bash, zsh, etc.). You're correct
         | that it's not something that's implemented as an external
         | program though; the "current directory" is state for a
         | currently running terminal session, so changing that state is
         | done via the shell interface (either directly by built-in
         | commands like cd or indirectly via external commands that use
         | those transitively).
        
           | machinestops wrote:
           | Note: Child processes can't change the working directory of
           | the parent. An external command (that is, not a shell
           | builtin, shell function, or externally loaded module) cannot
           | change the working directory, because they're launched as
           | child processes.
        
             | saghm wrote:
             | Good point. I'm not sure why I was thinking that it was
             | possible to do via some other command invoking `cd` or
             | something, but you're right that the only examples I can
             | think of are all using other builtins (e.g. invoking
             | `source` to have a script change the current state).
        
               | d3VwsX wrote:
               | There is a workaround I saw used by wcd, a tool for
               | changing directories. To install it you have to add a
               | wrapper function in your shell that executes the actual
               | wcd binary, and after possibly interacting with the user
               | to figure out what directory to change to the executable
               | will print out the destination, and then the wrapper
               | function will make the call to cd, affecting the shell it
               | runs in.
               | 
               | https://wcd.sourceforge.io/
        
       | lyxell wrote:
       | I'd like to add: Programs should not add files to your home
       | directory and should respect XDG_CONFIG_HOME and friends.
        
         | kelnos wrote:
         | I agree, but this article is -- as the author tries to make
         | clear -- descriptive, not prescriptive. She's listing out the
         | things she's seen commonly in applications, not trying to
         | convince applications that they should behave in certain ways.
        
       | chriswarbo wrote:
       | Ctrl-D for REPLs always bites me with GHCi. My usual approach to
       | quitting GHCi is:
       | 
       | - Press Ctrl-D, like normal
       | 
       | - Get confused when nothing happens
       | 
       | - Remember that it doesn't work in GHCi, so run `:q` instead
       | 
       | - Get an error message about "lexical error at character '\EOT'",
       | due to Ctrl-D inserting an invisible char at the start of the
       | input
       | 
       | - Try `:q` again, without any invisible prefix
       | 
       | - GHCi successfully quits
        
         | jeffrallen wrote:
         | You think that's hard, try getting out of vi when you're on a
         | keyboard from an unfamiliar country.
        
           | lilyball wrote:
           | Do keyboards in unfamiliar countries not have the colon key?
        
             | WolfeReader wrote:
             | That or the Q key
        
             | Philpax wrote:
             | Both the colon and Q keys can be in different places, which
             | can really mess with your muscle memory.
        
           | from-nibly wrote:
           | Or if you are like me and have caps mapped as escape.
           | 
           | Vim works really weird when you start typing random capital
           | letters after thinking you were moving somewhere.
        
           | saint_yossarian wrote:
           | There's also the `ZZ` / `ZQ` alternatives, so you just have
           | to find those letters :)
        
           | linhns wrote:
           | There's this: https://github.com/hakluke/how-to-exit-vim
        
         | marcosdumay wrote:
         | I always quit GHCi with Ctrl-D. I don't even know how else one
         | is supposed to.
         | 
         | Is there some setting that changes it?
        
         | tomsmeding wrote:
         | Are you on Windows?
        
           | chriswarbo wrote:
           | No, this is in Emacs shell-mode on Linux (various distros
           | over the years). Just tried a few other places: it works in
           | qterminal, and also doesn't work in eshell. I've been writing
           | Haskell for about 15 years, including my current job as a
           | full-time Haskell developer, and I never realised GHCi
           | supports Ctrl-D in some situations!
           | 
           | Every other REPL I've used handles this fine, e.g. for
           | quitting Python, Nix repl, SSH sessions, and even the shell
           | itself. Weird.
        
       | jeffrallen wrote:
       | If you are a young sysadmin, take the time to.learn Emacs. Not
       | because Emacs is good (but it is) but because deadline keys are
       | Emacs keys, so once you know Emacs you know shell, MySQL, etc.
       | 
       | Play your terminal like a piano . Your livelihood depends on it.
        
       | neilv wrote:
       | Additional suggestions:
       | 
       | * Respect the user's default foreground and background color.
       | Don't change them without good reason.
       | 
       | * If you use colors, make them legible regardless of what the
       | default background and foreground colors are, and regardless of
       | the terminal's color map.
       | 
       | * Don't use color as the only indication of something. The user's
       | terminal might not display it, and it probably won't be preserved
       | in copy&paste into notes.
       | 
       | * Use emoji only judiciously, if at all. Similar with gratuitous
       | non-ASCII characters. It doesn't display everywhere, it doesn't
       | paste well everywhere, and emoji can be a bit much when
       | copy&pasted into some notes.
       | 
       | * In a scrolling (non-full-screen) stdout-ish output, don't
       | delete important information that you showed temporarily. For
       | example, hiding warnings or filenames compiled, to display a
       | green checkmark for done. For another example, clearing the
       | screen of Web app build information (including package security
       | warnings!), to display a message that it's running in dev mode,
       | is also not wanted. People might want to see that information, or
       | copy&paste it into notes.
       | 
       | * If you went full angry fruit salad with your command line
       | program, because it's your baby, and you're having fun hamming it
       | up, that's fine, but please provide an easy preference setting
       | for people to opt out of that. Your program is probably only one
       | of many things on user's workstation display, where other
       | programs might be using color and visuals more meaningfully, so
       | animated throbbing red explosions for the code reformatter is a
       | bit much.
        
         | hatthew wrote:
         | As the article explicitly stated, these are _descriptive_ not
         | _prescriptive_ rules. They 're things that you can generally
         | assume all terminal programs already follow.
        
           | neilv wrote:
           | The ones I listed were mostly descriptive, until recent
           | years.
        
         | teddyh wrote:
         | > _please provide an easy preference setting for people to opt
         | out of that._
         | 
         | There is some effort to standardize this:
         | <https://bixense.com/clicolors/>
        
         | rodgerd wrote:
         | > * Don't use color as the only indication of something. The
         | user's terminal might not display it
         | 
         | The user may be colour-blind (30% of the population). The user
         | may be completely blind and relying on a screen reader.
        
           | Sharlin wrote:
           | 30%? As far as I know, about 10% of _male_ population has
           | red-green deficiency, in females it 's much rarer, and and
           | all the other forms of colorblindness are very rare
           | regardless of sex.
        
           | patmcc wrote:
           | Colourblindness is <10% of the population. But yes, quite
           | right it's not a good idea to rely on colour as the sole
           | indication of something important.
        
         | Terr_ wrote:
         | > Use emoji only judiciously, if at all.
         | 
         | That reminds me of an incredibly annoying bug I encountered a
         | few years ago involving Docker. One of the scripts being run
         | was outputting emoji to STDOUT, and this was causing the
         | interactive terminal to crash and thus the container to exit.
         | (This issue [0] has error-strings and simple repro tests.)
         | 
         | I'm not sure if the root cause ever got fixed, but I (and many
         | others) ended up making PRs for various open-source projects,
         | to grudgingly implement workarounds that compromised their
         | original artistic vision. :p
         | 
         | [0] https://github.com/docker-archive/toolbox/issues/695
        
         | rascul wrote:
         | > * If you use colors, make them legible regardless of what the
         | default background and foreground colors are, and regardless of
         | the terminal's color map.
         | 
         | This is kind of impossible. One can potentially provide for
         | customized colors, though.
        
           | ratorx wrote:
           | You can delegate the choice to the terminal by using ANSI
           | color codes [1]. Then the onus is on the user/terminal
           | developer to make sure the colors they've configured (or the
           | defaults provided) are reasonable.
           | 
           | A downside of this is that it is quite restrictive, there are
           | only like 8 colors.
           | 
           | EDIT: I missed the "regardless of the color map" bit, that is
           | a bit unreasonable. Either you trust the terminal emulator or
           | don't. I think trying to have it both ways is too much.
           | 
           | [1]: https://gist.github.com/JBlond/2fea43a3049b38287e5e9cefc
           | 87b2...
        
             | neilv wrote:
             | For example, I've seen a program that sets foreground color
             | to a symbolic (not RGB value) yellow, and doesn't set
             | background. While that combination might be legible on some
             | terminals, it's definitely not on all of them. Don't assume
             | that the user's terminal's color map makes all combinations
             | legible.
        
               | Vegenoid wrote:
               | What combinations _can_ be assumed to be legible? I think
               | if a terminal user has their colors configured so that
               | some of the 8 ASCII colors aren't legible on their
               | background, that's on them, with the exception of white
               | and black.
               | 
               | It seems like the only way to satisfy your ask is to not
               | use color at all.
        
               | neilv wrote:
               | The _default_ configurations of most terminals includes
               | illegible color combinations.
               | 
               | So I think it's not on every user to somehow design
               | optimal color palette settings on their computer that
               | work in all combinations (if they even can), but rather
               | on the developers of software not to say "Hey, I bet
               | every yellow would be legible on white" or "Yolo, I bet
               | yellow is legible on every background, so I'm just going
               | to set this foreground to yellow and not set background
               | at all."
        
               | jrockway wrote:
               | I've definitely noticed this going on. `jj log` uses the
               | right colors, but `watch --color jj log` somehow breaks
               | and prints black-on-black.
        
         | linhns wrote:
         | > Use emoji only judiciously, if at all. Similar with
         | gratuitous non-ASCII characters.
         | 
         | This deserves to be highlighted more. Nowadays some authors
         | keep assuming users use a patched font and build their tools
         | with that in mind, thus hampering ease of use significantly.
        
         | _kst_ wrote:
         | > Don't use color as the only indication of something. The
         | user's terminal might not display it, and it probably won't be
         | preserved in copy&paste into notes.
         | 
         | And some of us choose to disable color by default. (Yes, I'm
         | old-fashioned.)
        
       | ucarion wrote:
       | What does "cooked" mode mean in the context of this article?
        
         | kelnos wrote:
         | It means that the keystrokes you type aren't just immediately
         | handled to the application in raw form; something higher up the
         | chain (kernel, terminal emulator, shell) is pre-processing
         | ("cooking") them in some way, possibly taking actions before
         | (or in place of) passing the keystrokes to the application.
         | 
         | For example, if an application you're using has left things in
         | cooked mode, and you press ctrl+c, the application will never
         | "see" that keystroke; something higher up in the input chain (I
         | believe Linux's TTY driver) will see it, swallow it, and send
         | SIGINT to the application.
         | 
         | Applications can also put the tty into "raw mode", where this
         | won't happen; in that case the app is responsible for
         | implementing those "expected" keystroke behaviors, if it makes
         | sense for the application to do so.
        
           | lilyball wrote:
           | cooked mode also generally gives input to the program a line
           | at a time and handles things like backspace to delete
           | characters, and it handles program output too, doing things
           | like replacing \n with \r\n.
        
             | teddyh wrote:
             | No, line-by-line vs. character-by-character is a separate
             | mode from "cooked" mode. See "stty extproc".
        
               | lilyball wrote:
               | I read up on this and "cooked but character-by-character"
               | is apparently something called "cbreak mode" (or "rare
               | mode").
        
               | teddyh wrote:
               | According to stty(1), "cbreak" is a negative alias of
               | "icanon", which simply enables the special functions of
               | the 'erase' (backspace), 'kill' (Ctrl-U), 'werase'
               | (Ctrl-W), and 'rprnt' (Ctrl-R) keybindings. "cooked" mode
               | includes "icanon".
               | 
               | So no, I do not think that what you wrote is correct.
               | From what I can tell, the line-by-line mode is "extproc".
        
         | emmelaich wrote:
         | Do stty -a and have a look at the output. Do `man stty` for the
         | whole gory story.
         | 
         | Raw means send characters immediately, literally. Cooked imply
         | some some of the flags are on, specifically (from memory)
         | icanon, echo, among the `lflags`. Plus others.
         | 
         | Further down among `cchars` you'll see werase = ^w and kill =
         | ^u. Here kill means kill (erase) the line, not send a signal.
        
       | mattofak wrote:
       | In the same category of command line program guidelines:
       | https://clig.dev/
        
       | lieks wrote:
       | The main reason I enjoy CLIs so much more than GUIs (or eves TUIs
       | sometimes) is that it feels so consistent.
       | 
       | There are conventions, but following all the conventions in a CLI
       | is a lot easier than designing a good GUI. So they tend to be
       | higher quality as a result.
       | 
       | I spend a lot of time thinking how to bring this property to
       | GUIs, but my best answers are still "lots of effort" or "lower
       | your expectations".
        
         | tpmoney wrote:
         | A large part of this I think is that other than MacOS, GUI
         | conventions aren't heavily implemented by the OS and easy to
         | opt into. And because no other OS landed on "meta key for GUI
         | shortcuts" so there are a lot more conflicts (e.g. ctrl-c).
         | 
         | The Application Framework defaults which underly most native
         | macOS application build a lot of common keyboard controls in.
         | Use the default menu bar classes with the default basic
         | commands, and Command O, N, Q, X, C,V and probably others come
         | for free, you just implement the code that you need to for
         | those functions. Use a standard text field and you
         | automatically get Command/Option/Fn Left, Right, Up, Down for
         | navigation. It's more notabke when a macOS application doesn't
         | follow convention (e.g. InteliJ uses Shift-Opt up/down to move
         | lines rather than expand the selection by paragraph) than when
         | one does.
         | 
         | Windows does decently well on this front, but ctrl as a default
         | modifier can hurt terminal based app usage and there are a
         | number of UI frameworks even within the OS that appear to get
         | different defaults.
         | 
         | And in the Linux world, I think the only way you could do this
         | would be for someone to design (and a distro to standardize on
         | and port apps to) a full on application framework. The window
         | managers don't want to be in the business of dictating the
         | behavior of stuff in windows. The GUI toolkits don't want to be
         | in the business of defining os wide defaults and the DEs and
         | distros don't want to be in the business of if porting or
         | dictating UI frameworks. And realistically there's no one "on
         | high" that could make the sort of dictation that for example
         | "hence forth copy and paste will be Meta-C and Meta-V"
        
       | ruricolist wrote:
       | For forcing colorized output: I personally prefer pipetty (from
       | the colorized-logs package on Debian) to unbuffer, since unbuffer
       | merges stdout and stdin.
        
       | cbhl wrote:
       | > rule 3: REPLs should quit when you press Ctrl-D on an empty
       | line
       | 
       | If memory serves, this behavior depends on the OS. On Windows I
       | believe the norm there is to type "<Ctrl-Z><Enter>"
        
         | hatthew wrote:
         | I think it's assumed this article is about *nix, not windows
        
       | cpif wrote:
       | I can use Ctrl-A, Ctrl-E, and Ctrl-U in text fields in the lynx
       | browser, but not Ctrl-W.
       | 
       | I just checked to see if Ctrl-F and Ctrl-B work, and found that
       | the former kills one word forward and the latter acts like Ctrl-W
       | ought to?
        
       | jrockway wrote:
       | One that's missing is treating ~ as the home directory. This
       | appears to be a shell thing and not a POSIX API thing. For
       | example, this doesn't work:                 func main() {
       | if _, err := os.ReadFile("~/.bashrc"); err != nil {
       | log.Fatal(err)             }             fmt.Println("ok")
       | }
       | 
       | Meanwhile over in shell land:                 $ echo ~/~/~
       | /home/jrockway/~/~
       | 
       | The behavior is actually kind of amazing.
       | 
       | I mention it because while "yourprogram ~/path/to/file" always
       | works, having a repl that asks for a filename might not work.
       | I've seen a lot of software where this DOES work, so I think it
       | counts as a "most TUI programs do this" thing.
        
         | wahern wrote:
         | Tilde expansion is part of shell word expansion:
         | https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V...
         | 
         | Tilde expansion is the first operation in word expansion:
         | 
         | > The expansions that are performed for a given word shall be
         | performed in the following order: 1) Tilde expansion, parameter
         | expansion, command substitution, and arithmetic expansion shall
         | be performed, beginning to end. 2) Field splitting shall be
         | performed on the portions of the fields generated by step 1. 3)
         | Pathname expansion shall be performed, unless set -f is in
         | effect. 4) Quote removal, if performed, shall always be
         | performed last.
         | 
         | See
         | https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V...
         | 
         | To see the whole shell specification, goto the main page at
         | https://pubs.opengroup.org/onlinepubs/9799919799/. Select
         | "Shell & Utilities" from the top-left frame, then "Shell
         | Command Language" from the bottom-left frame.
        
         | Sharlin wrote:
         | That's a good point, particularly given that .. is handled for
         | you at the OS API level so you might be inclined to expect ~ is
         | too on POSIX.
        
         | Calzifer wrote:
         | In my opinion it should remain a shell thing. Adding tilde
         | expansion just complicates the implementation of the tool. Now
         | you need to know the current users home directory. And some
         | tools might implement it incomplete so that '~' works but
         | '~name' does not work.
         | 
         | In POSIX shells '~name' expands to the home directory of user
         | 'name'                 $ echo ~bin       /bin
         | 
         | Now your tool need a way to query home directory for any user
         | including system users. Depending on NSS configuration this is
         | more complicated than just reading /etc/passwd.
         | 
         | All for the rare case that someone passes a file path starting
         | with tilde and wants it to be expanded. IMO, when you provide a
         | file interactively you do so through a shell and tilde
         | expansion is handled by the shell and otherwise just provide
         | the actual file path and do not rely on the tool doing
         | additional expansions.
         | 
         | PS: I mixed up TUI and CLI program a bit in my head. For an
         | interactively used TUI program it might be beneficial to
         | implement tilde expansion (but then it should be as complete as
         | in Shells). A CLI program should not do magic stuff like tilde
         | expansion.
        
         | rascul wrote:
         | wordexp() might be interesting.
         | 
         | https://pubs.opengroup.org/onlinepubs/9799919799/functions/w...
        
         | jrockway wrote:
         | Oh, and I just remembered where this always burns me. It's
         | cases like this:                  ./myprogram
         | --config=~/.config/myprogram
         | 
         | Of course, many flags parsers are aware of this, or at least
         | accidentally aware of this, because you can write it in a form
         | that your shell will expand.                  ./myprogram
         | --config ~/.config/myprogram
         | 
         | For some reason, my muscle memory requires me to type the =. I
         | don't know why. It's probably a habit I picked up from a former
         | employer's flag parsing library. I also always use
         | --underscores_like_this instead of --hyphens-like-normal-people
         | for the same reason. Sigh!
        
         | GuB-42 wrote:
         | I would not do that to your own REPL unless you are really
         | serious about it, with good documentation, escaping, etc...
         | 
         | What if there is a file named "~"? What about "~username"? How
         | do you escape/quote it? What if $HOME is not set, or set to
         | something different than the actual user home directory? What
         | about Windows? Also, a lesser known fact is that typing "~."
         | after newline in ssh will force close the connection from the
         | client side and there is nothing the server can do about it, so
         | don't make it part of your workflow.
        
           | remram wrote:
           | It's even worse than that, there are others, much more likely
           | key combinations that are interpreted by the SSH client. For
           | example "~v" increases the verbosity of the SSH client, "~C"
           | opens a command line, etc.
           | 
           | Entering "~vicky/doc.txt" or "~Ricky/doc.txt" over SSH will
           | actually only write "icky/doc.txt", and entering
           | "~Chris/doc.txt" will have SSH print out it's internal
           | command-line's usage instructions in the middle of the TUI.
           | Screenshot from Vim: https://imgur.com/a/NCt0G9r
        
       | jez wrote:
       | Some more notes:
       | 
       | - If this is your first time hearing about the readline/emacs
       | keybindings like Ctrl-E and Ctrl-W, you'll be pleased to know
       | that most macOS input sources use these keybindings. If you're on
       | macOS, feel free to try Ctrl-E, Ctrl-W, or Ctrl-U in your
       | browser's address bar right now
       | 
       | - If you're using a command line program that doesn't support
       | _any_ line editing (e.g. no readline keybindings, and no other
       | keybindings), you can install the `rlwrap` program and launch the
       | REPL under rlwrap. For example Standard ML of New Jersey has a
       | REPL but no line editing functionality, but you can recover that
       | via `rlwrap smlnj`
       | 
       | - "don't use more than 16 colours" -- I would go so far as to say
       | "don't use more than 8 colors, or at least make your colors
       | configurable." Many popular color schemes, including Solaraized
       | and the default Base 16 color scheme, use the "bright" colors to
       | hold various shades of gray. What you think is "bright green"
       | might actually be the same shade of gray that normal text is
       | colored.
        
         | Ghoelian wrote:
         | > If you're on macOS, feel free to try Ctrl-E, Ctrl-W, or
         | Ctrl-U in your browser's address bar right now
         | 
         | Most browsers I've used close the current tab when you press
         | Ctrl-W. Actually, the terminal emulator I use, Alacritty, also
         | does this, and most file explorers that have tabs also do. Iirc
         | even windows explorer does this now, but it's been a while
         | since I've actually used windows.
        
           | Ghoelian wrote:
           | Actually no I'm wrong, I forgot that command isn't control on
           | a mac. Zen browser just does nothing when I press ctrl-W in
           | the address bar.
        
           | poincaredisk wrote:
           | >Actually, the terminal emulator I use, Alacritty, also does
           | this
           | 
           | This can't be right. I use ctrl+w all the time, and
           | occasionally use Alacritty. I'd notice if this shortcut
           | closed my terminal window (it's extremely annoying when I use
           | a web-based ssh, because I have this shortcut deep in my
           | muscle memory).
        
       | shmerl wrote:
       | _> don't use more than 16 colours_
       | 
       | We aren't in the '80s. Use true color if you want to, modern
       | terminals should support it (built in Linux tty is a weird
       | outlier that should have supported true color years ago).
       | 
       | But that also depends on the context. For example if something
       | implements its own TUI with a lot of elements - it makes more
       | sense to use more colors than the barebones set.
       | 
       | Most programs that do care about colors, check what terminal
       | capabilities are before using them.
        
         | zokier wrote:
         | As Evans explains, its not about technical capability, but
         | about respecting users choice of colors/theme.
        
       | adiabatty wrote:
       | One thing that I've noticed:
       | 
       | On UNIX, expanding globs (*.txt) is the shell's job.
       | 
       | On Windows, expanding globs is the program's job.
       | 
       | I used to have a bunch of four-line Python programs to,
       | essentially, run `flac --best --replay-gain *.wav`.
        
         | zokier wrote:
         | On Unix programs get arguments as array of strings. On Windows
         | programs get command line as single string.
         | 
         | Strictly speaking that does not explain where globbing happens,
         | but it does help understanding where they come from.
        
       | teddyh wrote:
       | See also _The Art of Unix Programming_ :
       | <http://www.catb.org/~esr/writings/taoup/html/>
        
       | xuhu wrote:
       | How did copy-pasting text between programs fit in the initial
       | shell design ? You can't paste from a remote tmux to a local one,
       | you can't mouse-copy multiple lines from a vim vertical split,
       | etc. Where's the terminal clipboard that works across programs ?
        
         | _kst_ wrote:
         | You can (usually) run your remote tmux in a window in your
         | local tmux session; then you can use the local tmux session's
         | copy/paste features.
         | 
         | I commonly run "ssh remote-system" in a tmux window, then
         | attach to a tmux session on the remote system.
         | 
         | If you nest tmux sessions like this, you have to type the
         | prefix character (Ctrl-B by default; I use Ctrl-Space) twice
         | for the nested session to see it.
        
       | eviks wrote:
       | Some of the rules codify many bad practices, from poor color
       | support to unergonomic keybindings. Given that the support isn't
       | universal and breaks in parts anyway, it's better to break it
       | competely and use something more ergonomic
        
         | poincaredisk wrote:
         | If you write a command line program, please don't introduce
         | random keybindings for simple actions, or unnecessarily fancy
         | colors when standard 8 would suffice. These rules (or rather
         | conventions) exist for a reason.
        
       | Sophira wrote:
       | One thing I'm wondering about is what text encoding the program
       | should use to output. I tend to write scripts that output
       | exclusively in UTF-8, but I realise this might not be a given.
       | (And, of course, the user's terminal expected encoding setting
       | doesn't necessarily mean that files should be written in that
       | encoding.)
       | 
       | Presumably, you would ideally output text in whatever encoding is
       | specified by the LANG environment variable, but this seems like
       | something that only comes with full i18n/l10n support, since it
       | also specifies the actual language to use.
       | 
       | Are there any actual established guidelines on this?
        
         | Sharlin wrote:
         | I think these days on POSIX you can just assume UTF-8. On
         | Windows the situation is... not great and entirely depends on
         | what terminal/shell combination you're using.
        
       | yencabulator wrote:
       | > then the operating system will return an EOF when you press
       | Ctrl-D on an empty line.
       | 
       | This is akshully not correct. Control-D makes the read(2) return
       | with the data currently in the input buffer. If there's no data
       | in the buffer, that results in a 0-length read, which is how EOF
       | is signaled.
       | 
       | Try this: run cat, type foo, press control-D. "foo" will be
       | echoed, without any newline.
        
         | wizzwizz4 wrote:
         | It _is_ correct, because when you press Ctrl+D on an empty
         | line, the OS will return an EOF. It 's not a complete answer,
         | but neither's yours, because you didn't consider what happens
         | when the OOM killer reaps cat. However, it's not _intended_ to
         | be a complete description, because it 's describing a UI
         | convention.
        
       | eschaton wrote:
       | Other rules for command line tools:
       | 
       | 1. Don't assume a terminal type. Look at `TERM` and use
       | termcap/terminfo or a library built atop them for anything beyond
       | line-oriented plain text output, or least assume a plain teletype
       | unless you specifically recognize the user's terminal type.
       | 
       | 2. Don't assume the presence of a terminal at all. Check
       | `isatty()` before doing anything fancy, and be sure to work
       | without a terminal so your tool can be used in pipelines and
       | called by other programs via `exec()`.
       | 
       | 3. Follow the common conventions in your arguments and output
       | structure. For example, if your tool takes an open-ended set of
       | arguments, support specifying a response file with
       | `@path/to/file` to avoid argument limits. If your tool supports
       | record-oriented output, support a `-0` argument to `NUL`-separate
       | the output for use with `xargs` in pipelines.
       | 
       | 4. Use the standard `<sysexits.h>` exit codes. They exist for a
       | reason and they make use of your tool within pipelines and
       | programs more straightforward because they make it easier to
       | trace why a failure occurred.
       | 
       | 5. Include both in-binary `--help`/usage information _and_ a man
       | page. Often a user will just need a quick refresher on argument
       | syntax, which is what the built-in help text is for; the man page
       | should be a comprehensive reference with examples. It should
       | *never* defer to a web page or GNU `info`--it's fine if those
       | exist too and are pointed out, but they should not be the primary
       | user reference.
       | 
       | Lots of Linux-oriented tools have one or more of these failure
       | modes, and behave poorly on real terminals that aren't VT100
       | derivatives or are awkward to use in anything but an interactive
       | setting.
        
         | _kst_ wrote:
         | > 1. Don't assume a terminal type. Look at `TERM` and use
         | termcap/terminfo or a library built atop them for anything
         | beyond line-oriented plain text output, or least assume a plain
         | teletype unless you specifically recognize the user's terminal
         | type.
         | 
         | I agree, but these days I think you can _mostly_ get away with
         | assuming VT100-compatible behavior.
         | 
         | > 4. Use the standard `<sysexits.h>` exit codes. They exist for
         | a reason and they make use of your tool within pipelines and
         | programs more straightforward because they make it easier to
         | trace why a failure occurred.
         | 
         | I just took a look at this header file. (It defines exit codes
         | starting at 64.) I'm not sure I've ever seen a program that
         | uses these codes. Many programs for UNIX-like systems just use
         | exit(1) for generic errors, or maybe something to distinguish
         | between data errors and usage errors such as unrecognized
         | command-line options. I mostly use Linux; maybe it's more
         | common on BSD-based systems (it appeared "somewhere after
         | 4.3BSD"). For example curl defines nearly 100 distinct error
         | codes; none of them are based on <sysexit.h>.
         | 
         | > 5. Include both in-binary `--help`/usage information and a
         | man page. Often a user will just need a quick refresher on
         | argument syntax, which is what the built-in help text is for;
         | the man page should be a comprehensive reference with examples.
         | It should _never_ defer to a web page or GNU `info`--it's fine
         | if those exist too and are pointed out, but they should not be
         | the primary user reference.
         | 
         | In practice, GNU `info` tends to be the primary reference for
         | GNU programs. The man page is often missing a lot of
         | information.
        
       | EasyMark wrote:
       | Another one that comes in really handy for ssh connections:
       | 
       | hit -> [return] ~ .
       | 
       | to break a jammed up ssh session. A lot of people don't seem to
       | know that one.
        
       ___________________________________________________________________
       (page generated 2024-12-13 23:02 UTC)