Warm tip: This article is reproduced from serverfault.com, please click

have arbitrary executable inherit errexit, if script is bash

发布于 2020-11-27 22:54:33

I have a folder of executable scripts, and some of them have Python shebangs, while others have Bash shebangs, etc. We have a cron job that runs this folder of scripts nightly, and the hope is that any error in any script will exit the job.

The scripts are run with something like: for FILE in $FILES; do ./$FILE; done

The scripts are provided by various people, and while the Python scripts always exit after an error, sometimes developers forget to add set -e in their Bash scripts.

I could have the for-loop use bash -e, but then I need to detect whether the current script is Bash/Python/etc.

I could set -e from the parent script, and then source scripts, but I still need to know which language each script is in, and I'd prefer them to run as subshells so script contributors don't have to worry about messing up the parent.

greping the shebangs is a short tweak, but knowing the flexibility of Bash, I'd be surprised if there weren't a way to "export" an option that affected all child scripts, in the same way you can export a variable. And, there have been many cases in general where I've forgotten "set -e", so it could be nice to know more options for fool-proofing things.

I see some options for inheriting -e for subshells involved in command substitution, but not in general.

Questioner
dcc310
Viewed
0
that other guy 2020-11-28 08:06:23

Disclaimer: Never, ever do this! It's a huge disservice to everyone involved. You will introduce failures both in scripts with meticulous error handling, and in scripts without it.

Anyways, no one likes being told "don't do that" on StackOverflow, so my suggestion would be to identify scripts and invoke them with their shebang string plus -e:

for f in ./*
do
  # Determine if the script is a shell script
  if [[ $(file -i "$f") == *text/x-shellscript* ]]
  then
    # Read the first line
    read -r shebang < "$f"

    # The script shouldn't have been identified as a shell script without
    # a shebang, but check anyways
    if [[ $shebang != "#!"* ]]
    then
      echo "No idea what $f is" >&2
      continue
    fi
    # Strip off the #! and run it with -e and the file
    shebang=${shebang#??}
    $shebang -e "$f"
  else
    # It's some other kind of executable, just run it directly
    "$f"
  fi
done

Here's a script with correct error handling that now stops working:

#!/bin/bash
my-service start
ret=$?
if [ $ret -eq 127 ]
then
  # Use legacy invocation instead
  start-my-service
  ret=$?
fi
exit "$ret"

Here's a script without error handling that now stops working:

#!/bin/sh
err=$(grep "ERROR" file.log)
if [ -z "$err" ]
then
  echo "Run was successful"
  exit 0
else
  echo "Run failed: $err"
  exit 1
fi