Reputation: 1075
zsh
doesn't recognize options set with -o
, but only when its in a shebang, and on Linux
.
The following script fails on zsh 5.0.2
, zsh 5.6
, and the latest git:
#!/bin/zsh -o pipefail
thiswillfail | echo 'hello, world'
echo $?
exit
Expected Output
hello, world
/home/prajjwal/script:2: command not found: thiswillfail
127
Actual Output
/bin/zsh: no such option: pipefail
What works
zsh 5.3
on MacOS Mojave
. This appears to be failing on every Linux
version I've tried so far, though./bin/zsh -o pipefail
on a terminalset -o pipefail
after the shebang in the script.What I've tried
.zshrc
's to ensure one of my settings isn't causing this.Aside
While I'm only trying to get pipefail
to work, this refuses to work with any other options that I try to set, even though all of them are mentioned in zshoptions
.
Upvotes: 3
Views: 3582
Reputation: 85887
This is a pitfall of trying to set options on the #!
line. The kernel only splits the shebang line at the first space.
When you say
#!/bin/zsh -o pipefail
the command that ends up being executed is "/bin/zsh" "-o pipefail" "/home/prajjwal/script"
.
The error message says that " pipefail"
(note the leading space) is not a valid option, which is correct: Only "pipefail"
is valid.
In this case a possible workaround is to cram everything into a single command line argument:
#!/bin/zsh -opipefail
But in general that's not possible and the #!
interface doesn't let you pass more than one extra argument, so your choices are to find other workarounds (e.g. using set
manually) or to write a wrapper script:
#!/bin/zsh
exec /bin/zsh -o pipefail /path/to/the/real/script
On some systems another alternative is available: env -S
. This option is not specified by POSIX and is not available on all systems. It works on systems using the GNU tools (including all standard Linux distributions) and FreeBSD, but not OpenBSD or NetBSD. I couldn't find any information about MacOS.
(Note: GNU env also supports --split-string
as an alternative (long) option name, but FreeBSD env does not.)
Usage:
#!/usr/bin/env -S /bin/zsh -o pipefail
This invokes env
with a single long argument ('-S /bin/zsh -o pipefail'
). Standard option processing treats it as the -S
option followed by an argument (' /bin/zsh -o pipefail'
).
In a simple case like this, env -S
splits the argument on spaces/tabs and treats the resulting list as if it had been part of the original command line in the first place:
env -S ' /bin/zsh -o pipefail'
# works the same as:
env /bin/zsh -o pipefail
In less simple cases you'll have to quote some characters (env -S
treats spaces, "
, '
, \
, $
specially, among others). In particular, it does not work like shell quoting. For details refer to the manual pages linked above.
Upvotes: 2