You can still use read
, you just need to print a prompt first. In zsh
, -p
indicates that input should be read from a coprocess instead of indicating the prompt to use.
You can do the following instead, which is POSIX-compliant:
printf '%s ' 'erase all directories? (y/n)'
read ans
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.
Best Answer
It's a bit weird, but it is documented:
The reason your first attempt hangs there is that it's reading from the terminal. Typing three characters on the terminal does unblock it. To read from standard input when you're asking for a limited number of characters rather than a whole line (with
-k
or-q
), you need to pass-u 0
explicitly.