When you press Ctrl+X, your terminal emulator writes the byte 0x18 to the master side of the pseudo-terminal pair.
What happens next depends on how the tty line discipline (a software module in the kernel that sits in between the master side (under control of the emulator) and the slave side (which applications running in the terminal interact with)) is configured.
A command to configure that tty line discipline is the
When running a dumb application like
cat that is not aware of and doesn't care whether its stdin is a terminal or not, the terminal is in a default canonical mode where the tty line discipline implements a crude line editor.
Some interactive applications that need more than that crude line editor typically change those settings on start-up and restore them on leaving. Modern shells, at their prompt are examples of such applications. They implement their own more advanced line editor.
Typically, while you enter a command line, the shell puts the tty line discipline in that mode, and when you press enter to run the current command, the shell restores the normal tty mode (as was in effect before issuing the prompt).
If you run the
stty -a command, you'll see the current settings in use for the dumb applications. You're likely to see the
echoctl settings being enabled.
What that means is that:
icanon: that crude line editor is enabled.
echo: characters you type (that the terminal emulator writes to the master side) are echoed back (made available for reading by the terminal emulator).
echoctl: instead of being echoed asis, the control characters are echoed as
So, let's say you type A B Backspace-aka-Ctrl+H/? C Ctrl+X Backspace Return.
Your terminal emulator will send:
AB\bC\x18\b\r. The line discipline will echo back:
AB\b \bC^X\b \b\b \b\r\n, and an application that reads the input from the slave side (
/dev/pts/x) will read
All the application sees is
AC\n, and only when your press Enter so it can't have any control on the output for
You'll notice that for echo, the first
^? with some terminals, see the
erase setting) resulted in
\b \b being sent back to the terminal. That's the sequence to move the cursor back, overwrite with space, move cursor back again, while the second
^H resulted in
\b \b\b \b to erase those two
^X (0x18) itself was being translated to
X for output. Like
B, it didn't make it to the application, as we deleted it with Backspace.
^M) was translated to
^M^J) for echo, and
^J) for the application.
So, what are our options for those dumb applications:
stty -echo). That effectively changes the way control characters are echoed, by... not echoing anything. Not really a solution.
echoctl. That changes the way control characters (other than
^M... and all the other ones used by the line editor) are echoed. They are then echoed as-is. That is for instance, the ESC character is send as the
0x1b) byte (which is recognised as the start of an escape sequence by the terminal),
^G you send a
\a (a BEL, making your terminal beep)... Not an option.
- disable the crude line editor (
stty -icanon). Not really an option as the crude applications would become a lot less usable.
- edit the kernel code to change the behaviour of the tty line discipline so the echo of a control character sends
\e[7m^X\e[m instead of just
\e[7m usually enables reverse video in most terminals).
An option could be to use a wrapper like
rlwrap that is a dirty hack to add a fancy line editor to dumb applications. That wrapper in effect tries to replace simple
read()s from the terminal device to calls to readline line editor (which do change the mode of the tty line discipline).
Going even further, you could even try solutions like this one that hijacks all input from the terminal to go through zsh's line editor (which happens to highlight
^Xs in reverse video) relying on GNU screen's
Now for applications that do implement their own line editor, it's up to them to decide how the echo is done.
bash uses readline for that which doesn't have any support for customizing how control characters are echoed.
info --index-search='highlighting, special characters' zsh
zsh does highlight non-printable characters by default. You can customize the highlighting with for instance:
For white on red highlighting for those special characters.
The text representation of those characters is not customizable though.
In a UTF-8 locale, 0x18 will be rendered as
\U7fffffff (two unassigned unicode code points) as
\u200b (a not-really printable unicode character) as
\x80 in a iso8859-1 locale would be rendered as