0

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.

Community
  • 1
  • 1
Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • I don't understand why you're doing two levels of output redirect, just use `2>&1` and it should be the same in both... or, is there a reason for using `1>&2`? – Elliott Frisch Dec 20 '13 at 04:19
  • It is just to explore why they behave differently. Yes, this is a toy example. I am not asking this question to "get something done" per se, I am asking it to gain a better understanding so that I can understand and explain how this works so that I can later use the power of knowledge to get many things done. – Steven Lu Dec 20 '13 at 04:21
  • And to answer your question specifically... `1>&2` is supposed to send stdout to stderr (which is the terminal), but zsh appears to still be sending a copy of it into the pipe as well. – Steven Lu Dec 20 '13 at 04:26
  • It does exactly that... you can't both redirect stdout to stderr (and stderr to stdout)... I'm not surprised bash and zsh interpret that impossibility differently. – Elliott Frisch Dec 20 '13 at 04:31
  • Yes you can because bash defines the order in which redirections are applied and the results are consistent and logical. I have demonstrated where zsh deviates from this, the question is what specifically is different in its behavior? – Steven Lu Dec 20 '13 at 04:32
  • "Portable" usually means sticking to the POSIX standard. A script using `bash` features still requires `bash`, regardless of how widely deployed `bash` may be. A POSIX-compliant script will (ok, should) run unmodified in any POSIX-compliant shell: `bash`, `dash`, `ksh`, `zsh`, etc. – chepner Dec 20 '13 at 13:30
  • See http://zsh.sourceforge.net/Doc/Release/Redirection.html#Multios – Stephane Chazelas Dec 23 '13 at 00:41

1 Answers1

3

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
Stephane Chazelas
  • 5,859
  • 2
  • 34
  • 31