It might be more helpful if the doc pointed out that there's no such thing as an ASCII EOF, that the ASCII semantics for ^D is EOT, which is what the terminal driver supplies in canonical mode: it ends the current transmission, the read
. Programs interpret a 0-length read as EOF, because that's what EOF looks like on files that have that, but the terminal driver refusing to deliver character code 4 and instead swallowing it and terminating the read isn't always what you want.
That's what's going on here: control character semantics are part of canonical mode, the mode where the terminal driver buffers until it sees a character to which convention assigns a special meaning. This is true of EOT, BS, CR and a host of others (see stty -a
and man termios
for alll the gory details).
read -N
is an explicit order to just deliver the next N characters. To do that, the shell has to stop asking the terminal driver for canonical semantics.
By the way, EOF isn't actually a condition a terminal can set, or enter.
If you keep reading past eof on anything else, you'll keep getting the EOF indicator, but the only EOF the terminal driver can supply is a fake one—think about it—if the terminal driver actually delivered a real EOF, then the shell couldn't keep reading from it afterwards either. It's all the same terminal. Here:
#include <unistd.h>
#include <stdio.h>
char s[32];
int main(int c, char**v)
{
do {
c=read(0,s,sizeof s);
printf("%d,%.*s\n",c,c,s);
} while (c>=0);
}
try that on the terminal, you'll see that the terminal driver in canonical mode just interprets EOT to complete any outstanding read, and it buffers internally until it sees some canonical input terminator regardless of the read buffer size (type a line longer than 32 bytes).
The text that's confusing you¸
unless EOF is encountered
is referring to a real EOF.
This usually happens when a program which set the terminal to raw input mode died unexpectedly and wasn't able to restore the terminal settings to their prior values.
A simple stty sane
should reset everything to normal.
As part of making the terminal "raw", the translation of Carriage-Return (^M
, \r
) to Line-Feed (^J
, \n
) is disabled by turning off the ICRNL
flag in the c_iflag
termios settings. This could also be done from the command line with stty -icrnl
.
The command line of a shell like bash is usually not affected by this, because bash makes itself the terminal raw and does its own key translation (in order to provide nice line-editing capabilities as moving the insertion point left and right, command line history, etc) and only restores the default terminal settings upon running other commands.
Best Answer
[[ -n "$line" ]]
tests if$line
(the variable just read byread
) is not empty. It's useful sinceread
returns a success if and only if it sees a newline character before the end-of-file. If the input contains a line fragment without a newline in the end, this test will catch that, and the loop will process that final incomplete line, too. Without the extra test, such an incomplete line would be read into$line
, but ignored by the loop.The
cmd1 || cmd2
construct is of course just like the equivalent in C. The second command runs if the first returns a falsy status, and the result is the exit status of the last command that executed.Compare:
and