Entering text in the terminal is complicated
2024-07-08T13:00:15+00:00The other day I asked what folks on Mastodon find confusing about working in the terminal, and one thing that stood out to me was “editing a command you already typed in”.
This really resonated with me: even though entering some text and editing it is
a very “basic” task, it took me maybe 15 years of using the terminal every
single day to get used to using Ctrl+A
to go to the beginning of the line (or
Ctrl+E
for the end – I think I used Home
/End
instead).
So let’s talk about why entering text might be hard! I’ll also share a few tips that I wish I’d learned earlier.
it’s very inconsistent between programs
A big part of what makes entering text in the terminal hard is the inconsistency between how different programs handle entering text. For example:
- some programs (
cat
,nc
,git commit --interactive
, etc) don’t support using arrow keys at all: if you press arrow keys, you’ll just see^[[D^[[D^[[C^[[C^
- many programs (like
irb
,python3
on a Linux machine and many many more) use thereadline
library, which gives you a lot of basic functionality (history, arrow keys, etc) - some programs (like
/usr/bin/python3
on my Mac) do support very basic features like arrow keys, but not other features likeCtrl+left
or reverse searching withCtrl+R
- some programs (like the
fish
shell oripython3
ormicro
orvim
) have their own fancy system for accepting input which is totally custom
So there’s a lot of variation! Let’s talk about each of those a little more.
mode 1: the baseline
First, there’s “the baseline” – what happens if a program just accepts text by
calling fgets()
or whatever and doing absolutely nothing else to provide a
nicer experience. Here’s what using these tools typically looks for me – If I
start the version of dash installed on
my machine (a pretty minimal shell) press the left arrow keys, it just prints
^[[D
to the terminal.
$ ls l-^[[D^[[D^[[D
At first it doesn’t seem like all of these “baseline” tools have much in common, but there are actually a few features that you get for free just from your terminal, without the program needing to do anything special at all.
The things you get for free are:
- typing in text, obviously
- backspace
Ctrl+W
, to delete the previous wordCtrl+U
, to delete the whole line- a few other things unrelated to text editing (like
Ctrl+C
to interrupt the process,Ctrl+Z
to suspend, etc)
This is not great, but it means that if you want to delete a word you
generally can do it with Ctrl+W
instead of pressing backspace 15 times, even
if you’re in an environment which is offering you absolutely zero features.
You can get a list of all the ctrl codes that your terminal supports with stty -a
.
mode 2: tools that use readline
The next group is tools that use readline! Readline is a GNU library to make entering text more pleasant, and it’s very widely used.
My favourite readline keyboard shortcuts are:
Ctrl+E
(orEnd
) to go to the end of the lineCtrl+A
(orHome
) to go to the beginning of the lineCtrl+left/right arrow
to go back/forward 1 word- up arrow to go back to the previous command
Ctrl+R
to search your history
And you can use Ctrl+W
/ Ctrl+U
from the “baseline” list, though Ctrl+U
deletes from the cursor to the beginning of the line instead of deleting the
whole line. I think Ctrl+W
might also have a slightly different definition of
what a “word” is.
There are a lot more (here’s a full list), but those are the only ones that I personally use.
The bash
shell is probably the most famous readline user (when you use
Ctrl+R
to search your history in bash, that feature actually comes from
readline), but there are TONS of programs that use it – for example psql
,
irb
, python3
, etc.
tip: you can make ANYTHING use readline with rlwrap
One of my absolute favourite things is that if you have a program like nc
without readline support, you can just run rlwrap nc
to turn it into a
program with readline support!
This is incredible and makes a lot of tools that are borderline unusable MUCH more pleasant to use. You can even apparently set up rlwrap to include your own custom autocompletions, though I’ve never tried that.
some reasons tools might not use readline
I think reasons tools might not use readline might include:
- the program is very simple (like
cat
ornc
) and maybe the maintainers don’t want to bring in a relatively large dependency - license reasons, if the program’s license is not GPL-compatible – readline is GPL-licensed, not LGPL
- only a very small part of the program is interactive, and maybe readline
support isn’t seen as important. For example
git
has a few interactive features (likegit add -p
), but not very many, and usually you’re just typing a single character likey
orn
– most of the time you need to really type something significant in git, it’ll drop you into a text editor instead.
For example idris2 says they don’t use readline
to keep dependencies minimal and suggest using rlwrap
to get better
interactive features.
how to know if you’re using readline
The simplest test I can think of is to press Ctrl+R
, and if you see:
(reverse-i-search)`':
then you’re probably using readline. This obviously isn’t a guarantee (some
other library could use the term reverse-i-search
too!), but I don’t know of
another system that uses that specific term to refer to searching history.
the readline keybindings come from Emacs
Because I’m a vim user, It took me a very long time to understand where these
keybindings come from (why Ctrl+A
to go to the beginning of a line??? so
weird!)
My understanding is these keybindings actually come from Emacs – Ctrl+A
and
Ctrl+E
do the same thing in Emacs as they do in Readline and I assume the
other keyboard shortcuts mostly do as well, though I tried out Ctrl+W
and
Ctrl+U
in Emacs and they don’t do the same thing as they do in the terminal
so I guess there are some differences.
There’s some more history of the Readline project here.
mode 3: another input library (like libedit
)
On my Mac laptop, /usr/bin/python3
is in a weird middle ground where it
supports some readline features (for example the arrow keys), but not the
other ones. For example when I press Ctrl+left arrow
, it prints out ;5D
,
like this:
$ python3
>>> importt subprocess;5D
Folks on Mastodon helped me figure out that this is because in the default
Python install on Mac OS, the Python readline
module is actually backed by
libedit
, which is a similar library which has fewer features, presumably
because Readline is GPL licensed.
Here’s how I was eventually able to figure out that Python was using libedit on my system:
$ python3 -c "import readline; print(readline.__doc__)"
Importing this module enables command line editing using libedit readline.
Generally Python uses readline though if you install it on Linux or through Homebrew. It’s just that the specific version that Apple includes on their systems doesn’t have readline. Also Python 3.13 is going to remove the readline dependency in favour of a custom library, so “Python uses readline” won’t be true in the future.
I assume that there are more programs on my Mac that use libedit but I haven’t looked into it.
mode 4: something custom
The last group of programs is programs that have their own custom (and sometimes much fancier!) system for editing text. This includes:
- most terminal text editors (nano, micro, vim, emacs, etc)
- some shells (like fish), for example it seems like fish supports
Ctrl+Z
for undo when typing in a command. Zsh’s line editor is called zle. - some REPLs (like
ipython
), for example IPython uses the prompt_toolkit library instead of readline - lots of other programs (like
atuin
)
Some features you might see are:
- better autocomplete which is more customized to the tool
- nicer history management (for example with syntax highlighting) than the default you get from readline
- more keyboard shortcuts
custom input systems are often readline-inspired
I went looking at how Atuin (a wonderful tool for searching your shell history that I started using recently) handles text input. Looking at the code and some of the discussion around it, their implementation is custom but it’s inspired by readline, which makes sense to me – a lot of users are used to those keybindings, and it’s convenient for them to work even though atuin doesn’t use readline.
prompt_toolkit (the library IPython uses) is similar – it actually supports a lot of options (including vi-like keybindings), but the default is to support the readline-style keybindings.
This is like how you see a lot of programs which support very basic vim
keybindings (like j
for down and k
for up). For example Fastmail supports
j
and k
even though most of its other keybindings don’t have much
relationship to vim.
I assume that most “readline-inspired” custom input systems have various subtle incompatibilities with readline, but this doesn’t really bother me at all personally because I’m extremely ignorant of most of readline’s features. I only use maybe 5 keyboard shortcuts, so as long as they support the 5 basic commands I know (which they always do!) I feel pretty comfortable. And usually these custom systems have much better autocomplete than you’d get from just using readline, so generally I prefer them over readline.
lots of shells support vi keybindings
Bash, zsh, and fish all have a “vi mode” for entering text. In a very unscientific poll I ran on Mastodon, 12% of people said they use it, so it seems pretty popular.
Readline also has a “vi mode” (which is how Bash’s support for it works), so by extension lots of other programs have it too.
I’ve always thought that vi mode seems really cool, but for some reason even though I’m a vim user it’s never stuck for me.
understanding what situation you’re in really helps
I’ve spent a lot of my life being confused about why a command line application I was using wasn’t behaving the way I wanted, and it feels good to be able to more or less understand what’s going on.
I think this is roughly my mental flowchart when I’m entering text at a command line prompt:
- Do the arrow keys not work? Probably there’s no input system at all, but at
least I can use
Ctrl+W
andCtrl+U
, and I canrlwrap
the tool if I want more features. - Does
Ctrl+R
printreverse-i-search
? Probably it’s readline, so I can use all of the readline shortcuts I’m used to, and I know I can get some basic history and press up arrow to get the previous command. - Does
Ctrl+R
do something else? This is probably some custom input library: it’ll probably act more or less like readline, and I can check the documentation if I really want to know how it works.
Being able to diagnose what’s going on like this makes the command line feel a more predictable and less chaotic.
some things this post left out
There are lots more complications related to entering text that we didn’t talk about at all here, like:
- issues related to ssh / tmux / etc
- the
TERM
environment variable - how different terminals (gnome terminal, iTerm, xterm, etc) have different kinds of support for copying/pasting text
- unicode
- probably a lot more