74

I have a script looks like:

c=0
for f in */*; do
cp -v "$f" "/myhome/CE$(printf '%0*d' 2 $BATCHNUM)-new-stuctures_extracted/test-$(printf '%0*d' 5 $c)"
c=$((c=c+1))
done

However, the user must provide a variable call BATCHNUM and otherwise I need to force this script stop running. It will be better if I could force the script that calls this script to stop too (or even the #1 script that calls #2 script which calls this script).

5 Answers 5

92

The quickest way is probably to add these two lines to the start of the script:

set -u # or set -o nounset
: "$BATCHNUM"

The first line sets the nounset option in the shell running the script, which aborts if you try to expand an unset variable; the second expands $BATCHNUM in the context of a no-op, to trigger the abort before doing anything else.

If you want a more helpful error message, you could instead write:

if [[ -z "$BATCHNUM" ]]; then
    echo "Must provide BATCHNUM in environment" 1>&2
    exit 1
fi

Or similar.

1
  • [[ -z "$BATCHNUM" ]] still triggers the error, at least in dash. I had to use [[ -z "${BATCHNUM:-}" ]] Dec 13, 2023 at 16:41
50

Here you want to check BATCHNUM is set and not null.

POSIX shell provide a Parameter Expansion for this job. Just adding this line before using BATCHNUM:

: "${BATCHNUM:?Variable not set or empty}"

or better to set the default value for BATCHNUM if user didn't provide one:

: "${BATCHNUM:=3}"
2
  • I'd just modify the message to repeat the var name, as it won't be displayed otherwise: : "${BATCHNUM:?Variable BATCHNUM not set or empty}", and follow it by another test & exit ( for ex: if [ -z "${BATCHNUM}" ]; then exit 1 ; fi ). Or better, just that later test, and display a message before the exit? Just displaying something when a var is unset is rarely useful (unless the script recovers from it later on. ) Apr 6, 2022 at 9:44
  • 2
    You can also omit the error message, and write "${BATCHNUM:?}". This will print a generic error message: bash: BATCHNUM: parameter null or not set Apr 13, 2022 at 11:04
6
[ -n "$BATCHNUM" ] || { kill "$PPID"; exit 1; }
#Unless $BATCHNUM is defined and unempty, ask parent process to exit and exit w/ 1

This will work on bash and in a POSIX sh. I prefer not to differentiate between empty variables and undefined variables (i.e., I don't like set -u, but that's just me).

6

The lines

if [ -z "$BATCHNUM" ]; then
    exit 2;
fi

check for empty $BATCHNUM. With $PPID you can do whatever harm you want to your parent (kill $PPID). For murdering your grandparent, you need to get the process id by other means, like looking at the data in /proc/$PPID.

However, if your parent dies, it sends a signal (SIGHUP) to you, so you have to trap it before you start killing anybody:

trap '' SIGHUP

Update: If you think you have to kill your parents, you doing it wrong. Just return a meaningfull exitcode. The parent script should check the called script's return code and react accordingly.

1
  • 5
    a bit brutal.... :)
    – user40780
    Sep 8, 2015 at 16:34
3

To test if BATCHNUM is defined, and exit if it isn't:

if [ -n "${BATCHNUM-a}" ]; then
  echo >&2 "Fatal error: BATCHNUM not set"
  exit 2
fi

If you also want to reject the case where BATCHNUM is empty, use ${BATCHNUM:+a} instead of ${BATCHNUM+a}. For information about the ${VARIABLE+TEXT_IF_NULL} parameter expansion construct, see e.g. the bash manual.

Don't kill the parent process. You don't know what the parent process is. If some script that calls this one needs to abort if this script aborts, make it check the exit status of this script. E.g. in script #2:

script3 || exit $?

or use set -e to abort the script if any command returns a failure (nonzero) status.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .