In bash, if I wanted to read, say 3, characters from a pipe, I could do:

... | read -n3

In zsh's read, the closest option seems to be -k:

-k [ num ]
Read only one (or num) characters. All are assigned to the first name, without word splitting. This flag is ignored when -q is present. Input is read from the terminal unless one of -u or -p is present. This option may also be used within zle widgets.

Note that despite the mnemonic ‘key’ this option does read full characters, which may consist of multiple bytes if the option MULTIBYTE is set.

And for -u and -p:

-u n
Input is read from file descriptor n.

Input is read from the coprocess.

A bare echo foobar | (read -k3; echo $REPLY) hangs waiting for input. -p fails with read: -p: no coprocess. Only the following works:

echo foobar | (read -k3 -u0; echo $REPLY)

This is the first time I'm seeing something that's more difficult to achieve in zsh than in bash.

Is there a simpler way to read N characters from stdin (whatever that might be) than this?

2 Answers 2


It's a bit weird, but it is documented:

-k [num]

(…) Input is read from the terminal unless one of -u or -p is present.

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.

echo foobar | ( read -u 0 -k 3; echo $REPLY )
  • That's pretty much the same command that's in the question. So no simpler way, I take it.
    – muru
    Aug 7, 2019 at 0:07
  • 1
    @muru I don't have an exhaustive knowledge of zsh, so I could be missing something, but I doubt it. One more option compared to bash is not the end of the world. If you're digging for things that are harder in zsh than in bash, try mapfile, I think some of its option combinations are rather awkward. Or history -d, it's always annoyed me that zsh has a nice history array but it's read-only. Aug 7, 2019 at 6:47

It works for me without the subshell:

% echo foobar | read -k3 -u0; echo $REPLY
  • The subshell is mostly irrelevant. The point is the extra options needed, and the need to specify the file descriptor.
    – muru
    Jun 27, 2018 at 7:47
  • I do find it interesting that help read makes no mention of reading from /dev/tty.
    – Tom Hale
    Jun 27, 2018 at 9:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.