Why doesn't the Enter key send EOL?
Essentially "because it's been done that way since manual typewriters". Really.
A manual typewriter had a carriage on which the paper was fed, and it moved forward as you typed (loading a spring), and had a lever or key which would release the carriage, letting the spring return the carriage to the left-margin.
As electronic data entry (teletype, etc) were introduced, they carried that forward. So the Enter key on many terminals would be labeled Return.
Line feeds happened (in the manual process) after returning the carriage to the left margin. Again, the electronic devices imitated the manual devices, making a separate line-feed operation.
Both operations are encoded (to allow the teletype to be more than a standalone device creating a paper type), so we have CR
(carriage-return) and LF
(line-feed). This image from ASR 33 Teletype Information shows the keyboard, with Return
on the right side, and Line-Feed
just to the left. Being on the right, it was the main key:
Unix came along later. Its developers liked to shorten things (look at all of the abbreviations, even creat
for "create"). Faced with a possibly two-part process, they decided that line-feeds only made sense if they were preceded by carriage-returns. So they dropped the explicit carriage returns from files, and translated the terminal's Return key to send the corresponding line-feed. Just to avoid confusion, they referred to line-feed as "newline".
When writing text on the terminal, Unix translates in the other direction: a line-feed becomes carriage-return / line-feed.
(That is, "normally": so-called "cooked mode", in contrast to "raw" mode where no translation is done).
Summary:
- carriage-return / line-feed is the sequence 13 10
- the device sends 13 (since "forever" in your terms)
- Unix-like systems change that to 13 10
- Other systems do not necessarily store just 10 (Windows largely accepts just 10 or 13 10, depending how important compatibility is).
While Thomas Dickey's answer is quite correct, Stéphane Chazelas correctly mentioned in a comment to Dickey's answer that the conversion is not set in stone; it is part of the line discipline.
In fact, the translation is completely programmable.
The man 3 termios man page contains basically all the pertinent information. (The link takes to Linux man-pages project, which does mention which features are Linux-only, and which are common to POSIX or other systems; always check the Conforming to section on each page there.)
The iflag
terminal attributes (old_settings[0]
in the code shown in the question in Python) has three relevant flags on all POSIXy systems:
INLCR
: If set, translate NL to CR on inputICRNL
: If set (andIGNCR
is not set), translate CR to NL on inputIGNCR
: Ignore CR on input
Similarly, there are related output settings (old_settings[1]
), too:
OPOST
: Enable output processing.OCRNL
: Map CR to NL on output.ONLCR
: Map NL to CR on output. (XSI; not available in all POSIX or Single-Unix-Specification systems.)ONOCR
: Skip (do not output) CR in the first column.ONLRET
: Skip (do not output) CR.
For example, you could avoid relying on the tty
module. The "makeraw" operation just clears a set of flags (and sets the CS8
oflag):
import sys
import termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
ch = None
try:
new_settings = termios.tcgetattr(fd)
new_settings[0] = new_settings[0] & ~termios.IGNBRK
new_settings[0] = new_settings[0] & ~termios.BRKINT
new_settings[0] = new_settings[0] & ~termios.PARMRK
new_settings[0] = new_settings[0] & ~termios.ISTRIP
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.IGNCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IXON
new_settings[1] = new_settings[1] & ~termios.OPOST
new_settings[2] = new_settings[2] & ~termios.CSIZE
new_settings[2] = new_settings[2] | termios.CS8
new_settings[2] = new_settings[2] & ~termios.PARENB
new_settings[3] = new_settings[3] & ~termios.ECHO
new_settings[3] = new_settings[3] & ~termios.ECHONL
new_settings[3] = new_settings[3] & ~termios.ICANON
new_settings[3] = new_settings[3] & ~termios.ISIG
new_settings[3] = new_settings[3] & ~termios.IEXTEN
termios.tcsetattr(fd, termios.TCSANOW, new_settings)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
although for compatibility's sake, you might wish to check if all those constants exist in the termios module first (if you run on non-POSIX systems). You can also use new_settings[6][termios.VMIN]
and new_settings[6][termios.VTIME]
to set whether a read will block if there is no pending data, and how long (in integer number of deciseconds). (Typically VMIN
is set to 0, and VTIME
to 0 if reads should return immediately, or to a positive number (tenth of seconds) how long the read should wait at most.)
As you can see, the above (and "makeraw" in general) disables all translation on input, which explains the behaviour cat is seeing:
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IGNCR
To get normal behaviour, just omit the lines clearing those three lines, and the input translation is unchanged even when "raw".
The new_settings[1] = new_settings[1] & ~termios.OPOST
line disables all output processing, regardless what the other output flags say. You can just omit it to keep output processing intact. This keeps output "normal" even in raw mode. (It does not affect whether input is automatically echoed or not; that is controlled by the ECHO
cflag in new_settings[3]
.)
Finally, when new attributes are set, the call will succeed if any of the new settings were set. If the settings are sensitive -- for example, if you are asking for a password on the command line --, you should get the new settings, and verify the important flags are correctly set/unset, to be sure.
If you want to see your current terminal settings, run
stty -a
The input flags are usually on the fourth line, and the output flags on the fifth line, with a -
preceding the flag name if the flag is unset. For example, the output could be
speed 38400 baud; rows 58; columns 205; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
On pseudoterminals, and USB TTY devices, the baud rate is irrelevant.
If you write Bash scripts that wish to read e.g. passwords, consider the following idiom:
#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0
The EXIT
trap is executed whenever the shell exits. The stty -g
reads the current settings of the terminal at the start of the script, so the current settings are restored when the script exits, automatically. You can even interrupt the script with Ctrl+C, and it'll do the right thing. (In some corner cases with signals, I've found that the terminal sometimes gets stuck with the raw/noncanonical settings (requiring one to type reset
+ Enter blindly at the terminal), but running stty sane
before restoring the actual original settings has cured that every time for me. So that's why it's there; a sort of added safety.)
You can read input lines (unechoed to the terminal) using read
bash built-in, or even read the input character-by-character using
IFS=$'\0'
input=""
while read -N 1 c ; do
[[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
input="$input$c"
done
If you don't set IFS
to ASCII NUL, read
built-in will consume the separators, so that c
will be empty. Trap for young players.