5

I'd like invoke a command inside a shell script for Continuous Integration purposes. Exit status 0 means succes, otherwise failure. I'm writing a wrapper script to run several commands and fail if any error has occurred from one of them.

However, one of the commands (3rd party software) does not conform to the "de facto" exit status != 1 on failure. It does, however, print its errors on stderr in case of a failure.

Current wrapper script, which should have worked fine if both mycommand and other-command will fail with exit status != 0 because of the -e switch:

#!/bin/bash -ex
mycommand --some-argument /some/path/to/file
other-command --other-argument /some/other/file

How do I check for anything being printed to stderr (to fail the main script)? Here's what I've tried:

  1. stderr output redirect to file, check file contents.
    Would like to avoid creating temporary files.
  2. Redirect stderr to subshell stdin, e.g.:

    mycommand 2> >(if grep .; then echo NOK; else echo OK; fi)
    

    This seems to work fine, however, I am not able to control the main shell here to exit, i.e. exit 1 won't exit the main program. Neither can I control variables outside the subshell to propagate its result out. Do I really have to create a named pipe or something?

  3. Set up extra file descriptors like this answer.
    Doesn't look very elegant to me, really.

Some 'requirements':

  • It should not fail on regular output on stdout (output there too).
  • I'd like to retain the otherwise useful output on stdout.
  • I'd like to keep any output currently on stderr printed (could be to stdout, but should not be hidded).

So it should behave like a wrapper that only exits with an unclean status, retaining printed output.

I was just hoping that there's something more elegant to check for anything in stderr. Bashisms acceptable.

  • My vague thought would be that if you modify attempt 2 to use a pipe instead, you'd get the exit code of the piped-through code, right? – Ulrich Schwarz Aug 23 '17 at 15:01
  • @UlrichSchwarz yeah, but there's also perfectly valid output on stdout. Forgot to mention that. I'd like to maintain all output. Will update Q. – gertvdijk Aug 23 '17 at 15:04
  • I agree with Ulrich; also, what do you mean however, I am not able to control the main shell here to exit? – Alex Aug 23 '17 at 15:04
  • @Alex clarified in Q I hope :) – gertvdijk Aug 23 '17 at 15:10
  • exit 1 exits the script... what do you mean, it won't exit the main program? – Alex Aug 23 '17 at 15:43
2

You could do (POSIXly):

if { cmd 2>&1 >&3 3>&- | grep '^' >&2; } 3>&1; then
  echo there was some output on stderr
fi

Or to preserve the original exit status if it was non-zero:

fail_if_stderr() (
  rc=$({
    ("$@" 2>&1 >&3 3>&- 4>&-; echo "$?" >&4) |
    grep '^' >&2 3>&- 4>&-
  } 4>&1)
  err=$?
  [ "$rc" -eq 0 ] || exit "$rc"
  [ "$err" -ne 0 ] || exit 125
) 3>&1

Using exit code 125 for the cases where the command returns with a 0 exit status but produced some error output.

To be used as:

fail_if_stderr cmd its args || echo "Failed with $?"
  • Beat me to it. Alright, I will edit in the link you provided. – Alex Aug 23 '17 at 16:19
0
# output "NOK" if standard error has any output; "OK" otherwise:
errlog=$(mktemp)
somecommand 1>> "$stdlog" 2> "$errlog"
if [[ -s "$errlog" ]]; then
    # File exists and has a size greater than zero
    echo "NOK"
else
    echo "OK"  
fi
# Done parsing standard error; tack it to the regular log
cat "$errlog" >> "$stdlog"
rm -f "$errlog"
  • Well, I think I've described this approach in my Q (creating temp files). I've posted the Q in the hope to avoid all that. – gertvdijk Aug 23 '17 at 15:53
  • In fairness, redirecting to a subshell is essentially creating temporary files; it just all happens behind the scenes. – DopeGhoti Aug 23 '17 at 15:54
  • In fairness, redirecting to a subshell is essentially creating temporary files; it just all happens behind the scenes. Really? I can't seem to find information on what kind of files and where I could find them. – gertvdijk Aug 23 '17 at 15:57

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.