[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)