Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a script that is composed of staged operations which work mostly in a start/stop/status manner.

The issue I have is that when starting, each of the operations can fail and I need to do a safe rollback using the stop actions.

How could I do this in bash?

I was thinking of something like the following (which doesn't work):

operation1_start() {}
operation2_start() {}
operation1_stop() {}
operation2_stop() {}

operation1_start && rollback=operation1_stop;$rollback
test_validity || $($rollback)
operation2_start && rollback=operation2_stop;$rollback
test_validity || $($rollback) 
share|improve this question
I don't think you want the extra $() around $rollback. that would be like putting $(operation1_stop) there. – user606723 Nov 30 '11 at 22:55

You will find these two shell programming techniques useful:

  • If you run set -e, then the shell exits immediately if a command returns a non-zero status (except in the cases where it's obviously meant, such as if or while conditionals).
  • If you run trap 'somecode' EXIT, then if the script exits (either explicitly, or implicitly because of set -e), somecode is executed first. On entry into somecode, $? contains the status of the last command.

Thus you can write something like

(set -e; trap 'abort operation a' EXIT; perform operation a; )

In bash, you can set a trap on ERR instead of EXIT; such traps are only executed if the shell exits due to set -e. Furthermore ERR traps are local to functions.

set -e
operation_a () {
  trap 'abort code' ERR
  perform operation a

When you can, it's easier to first prepare a draft and then perform an atomic operation to commit your transaction. For example, if you're writing to a file, write to a temporary file in the destination directory, then call mv to move the new file into place. This is a lot more robust than anything that requires cleanup in the form of rollback code, because the rollback will not be executed if your script dies because of a kill -9 or power failure.

share|improve this answer
How would that work for multiple operations? – Let_Me_Be Dec 1 '11 at 6:47
@Let_Me_Be If you can, go for the simplest approach: break up your task into separate transactions, and either commit or abort each. “Commit” means that the transaction (the subtask) completed successfully, and “abort” means that the transaction had no effect (whatever it started to do, it undid). – Gilles Dec 1 '11 at 11:08
Well yes, but when you have transactions A B C and C fails, you need to rollback B and A as well. – Let_Me_Be Dec 1 '11 at 14:28
@Let_Me_Be If you need that, then you have nested transactions. The whole ABC sequence is itself a transaction, and in your model you need to rollback the part of ABC that you've committed. – Gilles Dec 1 '11 at 14:44
Yes, I'm asking how to do that in your model. Because what I have is a set of operations that get the system from start state to end state. The are several correct combinations. – Let_Me_Be Dec 1 '11 at 16:54
up vote 1 down vote accepted

I actually managed to implement this using arrays. Seems to work fine (I even added an extra parameter telling the functions, that this is a rollback).

    echo "abc----$1";

    echo "cda----$1";

safe_rollback=( )


    while [ ${#safe_rollback[@]} -ge 1 ]; do
        ${safe_rollback[${#safe_rollback[@]}-1]} rollback;
        unset safe_rollback[${#safe_rollback[@]}-1];

add_rollback cda
add_rollback abc
share|improve this answer

Your operation actually sounds like a clone of a build process, where you have a start/status/continue/rollback/stop type actions based on various situations. You can probably achieve that effect more elegantly if you used Makefiles in conjunction with your shell scripts, because interdependencies that determine state are more elegantly definable there.

share|improve this answer
Unfortunately, the dependencies are dynamic. – Let_Me_Be Dec 1 '11 at 8:50

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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