Suppose I have a script like this:

command1 params
command2 params
command3 params

Now, as I understand, each of those commands will run independently of the return status of the previous one. But I want them all to run as a single transaction, i.e. if any previous command fails the script:

  1. should not proceed
  2. ideally revert actions done by previous commands

For the first question, I understand, that I can use && operator and chain all commands with it, or put if checks for $? after each command, but are these approaches the only options?

And what is the standard approach for the second question?

  • What if the command is `rm somefile‘? – Jeff Schaller Jan 22 '17 at 17:04
  • @JeffSchaller ... or outputs text to stdout. – Kusalananda Jan 22 '17 at 17:21
  • 3
    The rolling back is not possible automatically, you would have to invent logic for that yourself. – Martin Ueding Jan 22 '17 at 17:52
  • It depends a lot on the details of what you want to do with what privileges. It would be conceivable that you start by creating a LVM snapshot of all relevant partitions at the beginning, though I'd probably not do an automatic rollback that way on failure. (If failure is rare, it might be enough to know you have a disk state an admin can revert back to with manual intervention.) None of this applies if you're just an ordinary user who wants to do something transactionally. – Ulrich Schwarz Jan 22 '17 at 17:54

An alternative to chaining the commands together with && would be to run the script, or the section of the script, or sub-shell, with the -e option set (set -e, sometimes called the errexit option). This would cause the script or sub-shell to simply exit on an error:

( set -e
  command3 )

As for a rollback of already executed steps, this is probably impossible to achieve in a shell script.

Take, for example, a script that performs an exit or a system reboot, or even manipulates data on disk:

( set -e
  if (( $# == 0 )); then
    echo "No command line arguments!" >&2
  echo "We're cool"
  false )

( set -e
  case "$cmd" in
    restart)  sudo reboot ;;
    greet)    echo "Hi there" ;;
    *)        echo "Huh?" ;;
  false )

( set -e
  rm -f /tmp/important_tmp_file
  echo "oh oh"
  false )

There would not be any way of rolling back an exit from having been executed, or a reboot from having been performed, or to undelete a file automatically. Notice, too, that output to the terminal is also out of the hands of the shell and can't be called back.

| improve this answer | |
  • thanks, is there any mechanism to jump to some error handling block of commands on error? Something similar to try catch in programming languages? – Max Koretskyi Jan 23 '17 at 6:43
  • 1
    @Maximus There's the ERR trap. Using trap 'some_function' ERR will call some_function in the same situations where set -e will call exit (with set -e, the script or subshell will still exit, even if you trap the ERR). – Kusalananda Jan 23 '17 at 7:38
  • @Maximus ... it's not quite the same, but that's what's there. – Kusalananda Jan 23 '17 at 7:42
  • no problem, I'll read about it in depth – Max Koretskyi Jan 23 '17 at 7:43

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