6

I'm observing differences between zsh and bash when using read on macOS.

With bash this script

echo "characters" | while IFS= read -d '' -n 1 a; do printf %s "$a-"; done

Produces

c-h-a-r-a-c-t-e-r-s-

Whereas on zsh I have no output and no errors Is it possible to use read to iterate character by character?

user3589502
  • 61
  • 1
  • 3

1 Answers1

10

The options for the read command are significantly different between bash and zsh. In this case, the problem is that -n has completely different meanings between the two: in bash, it says how many characters to read, while in zsh it modifies the -p or -l options (which have to do with completion functions, and are irrelevant here).

In zsh, you use -k to specify the number of characters to read, but it also defaults to reading from the terminal rather than stdin, so you have to also add -u 0 to tell it to read from stdin.

Long story short: in zsh, use read -n '' -k 1 -u 0 somevar to read a single character from stdin.

BTW, there are lots more differences between read in bash vs zsh (and vs other shells). The POSIX standard only specifies the -r option; everything else is a nonstandard addition, and any similarity between different shells' extensions should be considered a happy accident.

Gordon Davisson
  • 34,084
  • 5
  • 66
  • 70
  • Additionally in general Zsh does not try to be POSIX-compliant in the first place. For me this means its support for `read -r` is also an accident or a deliberate courtesy. – Kamil Maciorowski Sep 05 '21 at 12:26
  • `zsh` isn't POSIX-compliant *by default*. It's possible to make it POSIX-compatible by invoking it, e.g, as `sh`. (Though to be honest, I've never tested how well the compatibility works.) – chepner Sep 05 '21 at 16:14
  • @chepner True, but zsh tends to follow POSIX *unless there's a reason to violate it*. For example, word splitting causes more problems than it solves, so zsh disables it by default. Though it occurs to me that, by that reasoning, you almost always want `-r`, so it'd be reasonable to make that the default (and then add a nonstandard option to turn backslash interpretation back). – Gordon Davisson Sep 05 '21 at 20:03