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 stty
command.
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 icanon
, echo
and 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 ^X
.
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 AC\n
.
All the application sees is AC\n
, and only when your press Enter so it can't have any control on the output for ^X
there.
You'll notice that for echo, the first ^H
(^?
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 ^
and X
characters.
The ^X
(0x18) itself was being translated to ^
and X
for output. Like B
, it didn't make it to the application, as we deleted it with Backspace.
\r
(aka ^M
) was translated to \r\n
(^M^J
) for echo, and \n
(^J
) for the application.
So, what are our options for those dumb applications:
- disable
echo
(stty -echo
). That effectively changes the way control characters are echoed, by... not echoing anything. Not really a solution.
- disable
echoctl
. That changes the way control characters (other than ^H
, ^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 \e
(^[
/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 ^X
(here \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 ^X
s in reverse video) relying on GNU screen's :exec
feature.
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.
For zsh
, see:
info --index-search='highlighting, special characters' zsh
zsh
does highlight non-printable characters by default. You can customize the highlighting with for instance:
zle_highlight=(special:fg=white,bg=red)
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 ^X
, \u378
, \U7fffffff
(two unassigned unicode code points) as <0378>
, <7FFFFFFF>
, \u200b
(a not-really printable unicode character) as <200B>
.
\x80
in a iso8859-1 locale would be rendered as ^�
... etc.
bash
it isreadline
handling that stuff, and for most others it is the tty driver. – mikeserv Oct 30 '15 at 16:51vi
andless
often highlight control characters differently from regular text. I will edit the question with this information. Thank you! – wefwefa3 Oct 30 '15 at 17:01zsh
which does that out of the box. – Stéphane Chazelas Nov 17 '15 at 15:57