Issue of read with -u and -k in zsh

I am developing a zsh script that uses read -k.
If I execute my script like this (echo a | myscript), it fails to get input.
Apparently it is due to the fact that -k uses /dev/tty as stdin invariably, and you must tell read to use stdin as in read -u0.

But then if I change it to -u0 (which makes previous case work) and execute my script without redirecting tty, it breaks the script, it simply does not behave as executing it without -u0.

EDIT: After debugging, it seems the issue is simply that after using -u0, the -k1 option does not read a single char and stops anymore. read works in this case as without -k, simply buffering all input and saving it as soon as an EOL arrives

EDIT2: After more debugging I know it’s something related to the raw mode not working with -u0. If I add stty raw/cooked before my read then it works (except enter keystroke is now handled with r not n), but then when I execute it with non-tty stdin it breaks.

Is there any way to make both modes compatible?

Indeed I would like to understand why the script behaves different at all, if either I read with -u0 or not, fd0 is by default the same as /dev/tty

Asked By: Whimusical


read -k (read N characters) and read -q (read y or n) have two modes of operation:

  • By default, they read from the terminal. They put the terminal in raw mode to read byte by byte (as many times as necessary to read the requested number of characters) rather than line by line.
  • They can be instructed to read from an existing file descriptor (-u with a number, or -p to read from the pipe used to communicate with the current coprocess). In this case, they just read from the file descriptor.

There’s no option to tell zsh to read from a specific source, but change the terminal mode if reading from a terminal. You can arrange it yourself, though: check if standard input is a terminal, and don’t pass -u0 if it is.

if [[ -t 0 ]]; then
  read -k1 …
  read -k1 -u0 …
Categories: Answers Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.