I need to execute a number of piped shell commands from a non-BASH script (namely PHP script) like these:

command1 | command2 | command3

so that, if command1 fails with a non-zero exit code, each other command fails too. So far, what I've come up with is:

set -o pipefail && command1 | command2 | command3

But even though it runs fine from the terminal, it produces this if executed from the script:

sh: 1: set: Illegal option -o pipefail

migrated from stackoverflow.com Nov 24 '12 at 18:12

This question came from our site for professional and enthusiast programmers.

  • It appears that /bin/sh doesn't like set -o pipefail. Is it actually bash in disguise, or is it a different shell? When bash is run as /bin/sh, does it accept set -o pipefail? – Jonathan Leffler Nov 24 '12 at 17:24
  • @JonathanLeffler When I run /bin/sh set -o pipefail it says /bin/sh: 0: Can't open set (same thing with sudo). Hope I tested it right. The system is Ubuntu 12. – Desmond Hume Nov 24 '12 at 17:36
  • You'd need to try /bin/sh -c "set -o pipefail"; as it was, the shell was trying to execute a script in the current directory called set and it didn't find it. – Jonathan Leffler Nov 24 '12 at 19:29
  • @JonathanLeffler /bin/sh -c "set -o pipefail" isn't working, however bash -c "set -o pipefail" does. – Desmond Hume Nov 24 '12 at 19:46
  • So, your problem is that the script is run by /bin/sh which doesn't recognize set -o pipefail. Consequently, you'll need to ensure that the script is run by /bin/bash instead of /bin/sh. Or, if you're confident, brave — and probably foolhardy — change /bin/sh to be a link to, or copy of, /bin/bash instead of whichever shell it currently is linked to or a copy of. If you're sure that /bin/sh is bash, then you're using behaviour which bash doesn't expose when run as /bin/sh; use bash as bash. – Jonathan Leffler Nov 24 '12 at 19:50

From the Bash command line you would need to invoke a subshell to avoid pipefail being set afterwards:

$ (set -o pipefail && command1 | command2 | command3)

This would limit the effect of the pipefail option to the subshell create by the parentheses (...).

A real example:

$ (set -o pipefail && false | true) && echo pipefail inactive || echo pipefail active
pipefail active

If you use an explicit shell call with the -c option you do not need a subshell, either with bash or with an sh alias to bash:

$ bash -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active
$ sh -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active

Since your sh does not accept the pipefail option, I would have to assume that it is either some older or modified version of bash - or that it is actually some other shell completely.

  • 1
    Is $ in the first piece a part of the command is it just a shell prompt? Tried both ways, didn't really work. The bash -c way always works, not too elegant tho.. – Desmond Hume Nov 24 '12 at 19:36

Not sure why it wasn't mentioned above, but it is possible to explicitly unset pipefail, using set +o pipefail.

set -o pipefail
command1 | command2 | command3
set +o pipefail

If you are executing a fragment, and unsure about the pipefail already set, you can use this with the subshell as previously suggested:

# explicitly execute with pipefail set
(set -o pipefail ; command1 | command2 | command3 )
# explicitly execute with pipefail unset
(set +o pipefail ; command1 | command2 | command3 )

You can use $(command1) combined with $? :

a=$(echo "a")
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Prints "abc"

a=$(ls unexitingDir)
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Prints "".

"[ $? -eq 0 ] &&" means that the following command is executed only if the previous one succed.

That dosen't answer your question but it's a solution to your problem.

  • Does it mean that I would always need to store the output of each of the commands into a variable? – Desmond Hume Nov 24 '12 at 15:44
  • Always I don't know but with my solution yes you will (if you need it for your next command). Unless you can just write command1 && command2 && command3 – cglacet Nov 24 '12 at 18:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.