I/O redirection differences between zsh and bash

2019-09-04 16:59发布

问题:

I use zsh at the command-line but the shell scripts I write run bash so that they are portable.

I was learning about IO redirection from here when I realized this difference:

Note that the command is just an arbitrary one whose first line of output is over stderr and the second line comes over stdout.

zsh on OS X:

% ls -ld /tmp /tnt 1>&2 2>&1  | sed -e 's/^/++/'
ls: /tnt: No such file or directory
++ls: /tnt: No such file or directory
lrwxr-xr-x@ 1 root  wheel  11 Oct 19  2012 /tmp -> private/tmp
++lrwxr-xr-x@ 1 root  wheel  11 Oct 19  2012 /tmp -> private/tmp

bash:

bash-3.2$ ls -ld /tmp /tnt 1>&2 2>&1  | sed -e 's/^/++/'
ls: /tnt: No such file or directory
lrwxr-xr-x@ 1 root  wheel  11 Oct 19  2012 /tmp -> private/tmp

I'm having a hard time figuring out zsh's output.

Also, on a Linux the output order's slightly different for zsh:

% ls -ld /tmp /tnt 1>&2 2>&1  | sed -e 's/^/++/'
ls: cannot access /tnt: No such file or directory
drwxrwxrwt. 13 root root 4096 Dec 19 23:11 /tmp
++ls: cannot access /tnt: No such file or directory
++drwxrwxrwt. 13 root root 4096 Dec 19 23:11 /tmp

bash output is identical though.

More experimentation in zsh:

% ls -ld /tmp /tnt 1>&2 | sed -e 's/^/++/'
ls: /tnt: No such file or directory
lrwxr-xr-x@ 1 root  wheel  11 Oct 19  2012 /tmp -> private/tmp
++lrwxr-xr-x@ 1 root  wheel  11 Oct 19  2012 /tmp -> private/tmp

% ls -ld /tmp /tnt 2>&1 | sed -e 's/^/++/'
++ls: /tnt: No such file or directory
++lrwxr-xr-x@ 1 root  wheel  11 Oct 19  2012 /tmp -> private/tmp

That last one there produces identical results to bash.

I figure I should prefer learning bash's behavior inside-out before delving into how zsh ticks, but this isn't really ideal because chances are at least half of the IO redirection I expect to be doing will be spur-of-the-moment hacks that I surely will want to try from the zsh prompt. And I'm actually pretty invested in zsh because I have a ton of custom plugins and it would be a major effort at this point to do a big switch back to bash.

回答1:

That's due to zsh's mult_ios feature.

In zsh, when a fd is redirected twice, zsh implements an internal tee:

ls > foo > bar

Sends the output of ls to a pipe and zsh feeds it to both foo and bar.

It can get confusing with pipes.

ls > foo | cmd

Sends the output to both foo and cmd.

You can disable it with:

setopt no_mult_ios