0

I was debugging a shell script, and the problem was that the following code

if curl doesntexist -i &> /dev/null;
then
        echo True 
else
        echo False
fi 

Is, if running zsh, not equivalent to:

if curl 6 -i 1> /dev/null 2> /dev/null;
then
        echo True 
else
        echo False
fi 

The later echos True as expected, but if I redirect with &>, the output is false. I do not understand this behaviour, for example here it says that

&>name is like 1>name 2>name

Can someone explain why the two snippets do not behave the same if running in zsh? From zsh docu it says that it should also redirect stdout and stderr, sounds like it should do the same as in bash:

&> Redirects both standard output and standard error (file descriptor 2) in the manner of ‘> word’

Adrian
  • 13
  • 2
  • 1
    cannot reproduce, works as expected - both give False – kwarunek Dec 19 '19 at 10:38
  • 1
    Are you sure you run in `bash`? The `&>` does not work in other shells in the same way. – DaBler Dec 19 '19 at 10:57
  • yeah just noticed. Its because I was running zsh – Adrian Dec 19 '19 at 10:59
  • Cannot replicate, both give `False` **as expected** on `zsh 5.7.1` – donkopotamus Dec 19 '19 at 11:12
  • How do you run the script? I do `./test.sh` in my zsh shell, and I get True for one of the scripts and False for the other. – Adrian Dec 19 '19 at 11:14
  • Okay I see running with `zsh test.sh` works, in zsh running with `./test.sh` breaks, but in bash I can run it with `./test.sh` – Adrian Dec 19 '19 at 11:18
  • 1
    Does `test.sh` have a shebang (`#!/bin/zsh` or similar)? If not, `./test.sh` will be executed with `bash` when run from `bash`, but with `/bin/sh` when run from `zsh`. – chepner Dec 19 '19 at 12:58
  • 1
    `&>` is not a recognized redirection operator in `/bin/sh`; it's probably being parsed as two separate commands: `curl` run in the background with `&` (which always has 0 as its exit status; the exit status of `curl` itself isn't relevant), and an empty command `> /dev/null` which also always has an exit status of 0: hence the `True` path being taken. – chepner Dec 19 '19 at 13:00

1 Answers1

1

I suspect your script does not have a shebang to indicate which shell should be used to execute it, that you have made it executable, and are executing it with something like ./test.sh. If that is the case, adding something like #!/bin/bash or #!/bin/zsh will solve the problem.


Without a shebang, what actually executes your script depends on which shell you are executing it from. bash will execute the script with bash. zsh, however, will execute the script with /bin/sh.

In bash, &> is a redirection operator that redirects both standard error and standard input to the same file.

In /bin/sh, though, it is not a redirection operator. The command curl doesntexist -i &> /dev/null is parsed as two separate commands:

curl doesntexist -i &
> /dev/null

The first runs curl in the background, and immediately returns with a 0 exit status. (The exit status of curl itself is never considered.) The second command is a valid empty command that simply opens > /dev/null for writing, then exits with a 0 exit status.

As a result, no matter what curl might do, the exit status that if cares about is just the last one in the list, that of > /dev/null. Since that is 0, you get the True path.

In bash, where &> is the valid redirection operator, if looks at the exit status of curl as expected.

chepner
  • 497,756
  • 71
  • 530
  • 681