•6 min read

Unwritten Rules of Terminal Programs

Unwritten Rules of Terminal Programs

I've spent countless hours in terminals, and something odd keeps striking me. Despite no central command telling developers how to build CLI tools, they all speak roughly the same language. q quits. Ctrl-C cancels. Colors disappear when you pipe output somewhere else.

This isn't accident. It's evolution.

The consistency comes from POSIX standards, old Unix conventions, and sheer momentum. Once enough tools agree on something, disagreement becomes a user experience problem. Below are the patterns I've noticed, the ones that make the command line feel intuitive even when you're using something completely new.

Audio Briefing
0:00 / 0:00
Part of a Series

Modern Linux & Terminal Mastery Series

Part 4 of 4

Descriptive, Not Prescriptive

These rules are based on real-world observation, not documentation. Modern efforts like the CLI Guidelines try to formalize what developers already knew intuitively.

The Unwritten Rules

Rule 1: Quit on Ctrl-C (Noninteractive)

If a program is noninteractive, pressing Ctrl-C should kill it immediately via a SIGINT signal. Think ping 127.0.0.1 or curl https://example.com. You hit Ctrl-C, the program dies, you get your prompt back.

Interactive programs handle this differently. Shells, text editors like vim, and similar tools catch the signal and use it for something else. In vim, Ctrl-C cancels whatever you're typing. It doesn't exit because exiting would mean losing unsaved work. That's a deliberate choice, not a bug.

Rule 2: 'q' Quits TUIs

Text-based User Interfaces like less, top, htop, man, and vim all respond to the q key for quitting. Try it. Open less /etc/passwd, type q, you're back at your shell. Same with top. Same with htop.

Why q and not Ctrl-Q? Ancient habit, probably. q sits left-hand home row, it's fast, and it's mnemonically obvious. This convention is so strong that breaking it feels like a personal insult.

Rule 3: Ctrl-D Exits REPLs

Read-Eval-Print Loops behave differently. When you're in Python, Node's REPL, or IRB (Ruby's interactive shell), try pressing Ctrl-D on an empty line. The REPL closes. Why?

Ctrl-D sends an EOF marker, an End of File character. REPLs interpret this as "the user is done giving input" and shut down cleanly. It's graceful termination, not a violent kill like Ctrl-C.

Try it in a real terminal. Type python3, wait for the >>> prompt, press Enter, then Ctrl-D. Gone. That's the convention.

Color and Display Guidelines

Rule 4: Stick to 16 Colors

Most well-behaved terminal programs use only 16 colors. Those are the ones every terminal theme supports. True color (24-bit, 16 million colors) and 256-color modes exist, but they can conflict with user color schemes and accessibility settings. When in doubt, stay conservative.

Rule 6: Disable Colors in Pipes

This one bit me once and I've never forgotten it. When stdout is a terminal, print colorful output. When stdout is a pipe or file, strip the color codes. Otherwise, developers run yourcommand | grep something and see garbage like \033[31mError\033[0m in their logs.

Detect this with !isatty(stdout). Every language has an equivalent: Python has sys.stdout.isatty(), Node has process.stdout.isatty(). Use it.

Keybindings and Input Conventions

Rule 5: Vaguely Support Readline Keybindings

Users bring expectations from bash. These should mostly work:

  • Ctrl-E jumps to the end of the line
  • Ctrl-A jumps to the beginning
  • Ctrl-W deletes the last word

Do you need full readline compatibility? Probably not. But users will try Ctrl-A in your tool expecting it to work. When it doesn't, they notice.

What's the "last word" anyway? For most programs it's space-delimited. Ctrl-W in bash delete everything back to the last space. That behavior comes from cooked mode in the OS terminal driver, so it's what users expect.

Rule 7: A Hyphen Means Stdin or Stdout

This one surprised me when I first noticed it. Many CLI tools accept a single hyphen as a special argument meaning "read from stdin" or "write to stdout."

Some examples: cat - reads from stdin tar cf - /path |-gzip > file.tar.gz streams archive to stdout diff file1 - compares file1 against stdin

It's a convention, not a requirement. But when you see it, you'll understand what the author meant.

Rule 8: Modern Replacements Follow the Old Rules

Notice how modern Rust rewrites like bat (for cat), rg (for grep), and fd (for find) strictly obey these exact same unwritten rules? They add syntax highlighting and speed, but they still drop color when piped, still quit on Ctrl-C, and still accept - for stdin. They succeed because they respect the ghosts of POSIX past.

Advertisement

Why Does This Matter?

Here's the thing. These patterns aren't rules someone wrote down and enforced. They're emergent. When enough tools do something one way, users learn that way. New tools inherit the same behavior because defying it creates friction.

I remember the first time I used a tool that didn't respond to q for quitting. It was some custom TUI for managing Docker containers. I hit q. Nothing. I hit Ctrl-C. Nothing. I had to kill it with Ctrl-Z and kill -9. It felt broken, even though it probably worked "correctly."

That's the point. In the terminal, convention IS correctness. If your tool fights user expectations, it's wrong, regardless of what your documentation says.

The CLI Guidelines project tries to formalize some of this. It's worth reading. But honestly, the fastest way to learn these conventions isn't reading at all. It's using the terminal daily. The patterns sink in through muscle memory.

Learning by Doing

Install a new tool? Spend two minutes hitting q, Ctrl-C, Ctrl-D, and scrolling with spacebar. Most of the time, you'll discover the conventions without touching the manual.

A Pattern Worth Noticing

Next time you're exploring a new CLI tool, try this. Watch how it handles interrupts. Notice whether colorful output vanishes when you pipe it somewhere. Observe what happens when you press Ctrl-A in an input field.

You'll start seeing the patterns everywhere. Because they're not accidents. They're the accumulated decisions of decades, converging on a shared understanding of how terminals should work.

And that strangeness I mentioned at the start? The "strange harmony" of unrelated commands behaving consistently? It's not strange at all when you realize it comes from the same source as language itself. Convention. Repetition. Time.

Your tools are speaking the same dialect because we all learned it from each other.

Related Terminal Posts

You Might Also Like

Share this article:

Stay Updated

Get the latest posts delivered straight to your inbox.

Free Developer Utilities

Free In-Browser Developer Tools

Clean AI CLI logs, build cron expressions, decode JWTs, and calculate chmod permissions offline.

Explore Tools
Advertisement
Creating a Modern Terminal Setup
terminal

Creating a Modern Terminal Setup

A minimal terminal setup that actually sticks: Starship prompt, zsh-autosuggestions, syntax highlighting, and the hard-learned lesson about the dotfile rabbit hole.

Read more