Categories: Uncategorized

[Shell][Bash]Try Catch Finally

Background

Error handling in Bash is little bit difficult.
But, “set command” and “trap command” help us to do it.

Environment

  • OS
    • Linux 2.6.32-279.el6.x86_64 #1 SMP Fri Jun 22 12:19:21 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

Content

Try-Catch-Finally

set command reference

trap command reference
#!/bin/bash
function main() {
        trap catch ERR
        echo "[INFO]Main"
        return 0
}

function catch() {
        echo "[ERROR]Fail"
}
function finally() {
        echo "[INFO]Finish"
}

# Entry Point
set -eu
trap finally EXIT
main

The source code result is following.
$ ./main.sh
[INFO]Main
[INFO]Finish
Error handling check.
Add error command to main function and try to run it.
#!/bin/bash
function main() {
        trap catch ERR
        echo "[INFO]Main"
        diff test
        return 0
}

function catch() {
        echo "[ERROR]Fail"
}
function finally() {
        echo "[INFO]Finish"
}

# Entry Point
set -eu
trap finally EXIT
main
Result ist following.
$ ./main.sh
[INFO]Main
diff: missing operand after `test'
diff: Try `diff --help' for more information.
[ERROR]Fail
[INFO]Finish

Retry

Easy to add retry function with inserting “main” catch function.
#!/bin/bash
function main() {
        trap catch ERR
        echo "[INFO]Main"
        return 0
}

function catch() {
        echo "[ERROR]Fail"
        echo "[INFO]Retry"
        main

}
function finally() {
        echo "[INFO]Finish"
}

# Entry Point
set -eu
trap catch ERR
trap finally EXIT
main
Let’s check error handling part with following program which has an error.
#!/bin/bash
function main() {
        trap catch ERR
        echo "[INFO]Main"
        diff test
        return 0
}

function catch() {
        echo "[ERROR]Fail"
        echo "[INFO]Retry"
        main

}
function finally() {
        echo "[INFO]Finish"
}

# Entry Point
set -eu
trap catch ERR
trap finally EXIT
main
Result is following.
$ ./main.sh
[INFO]Main
diff: missing operand after `test'
diff: Try `diff --help' for more information.
[ERROR]Fail
[INFO]Retry
[INFO]Main
diff: missing operand after `test'
diff: Try `diff --help' for more information.
[INFO]Finish

unit test

You can write unit test with this structure easily.
shunit2 will help you. Concrete source code is following.

main.sh

#!/bin/bash
function main() {
        trap catch ERR
        echo "[INFO]Main"
        lib_main
        return 0
}
function catch() {
        echo "[ERROR]Retry"
        main
}
function finally() {
        if [ ! $? -eq 0 ]; then
                TITLE="error"
                TO=abc@example.com
                FROM=abc@example.com
                echo -e "Please check log file." | mail -s "$TITLE" -r "$FROM" "$TO"
                echo "[ERROR]Alert mail is sent to $TO ."
        fi

        echo "[INFO]Finish"
}

# Entry Point
SOURCE_PATH="$(cd $(dirname $0);pwd)"
source $SOURCE_PATH/lib.sh
set -eu
trap finally EXIT
main

lib.sh

#!/bin/bash
function lib_main() {
        echo "[INFO]Lib Main"
        touch "test.log"
        return 0
}

test.sh

#!/bin/bash
function oneTimeSetUp() {
  echo "[INFO]Setup"
  source ./lib.sh
}
function testLibMain() {
  lib_main
  test -e test.log
  ${_ASSERT_EQUALS_} "TestFileExists" $? 0
}
. "path/to/shunit2/shunit2"
main.sh result is following.
$ ./main.sh
[INFO]Main
[INFO]Lib Main
[INFO]Finish
test.sh result is following.
[INFO]Setup
testLibMain
[INFO]Lib Main

Ran 1 test.

OK

Conclusion

You can add error handling and retry function to Bash script with “set command” and “trap command”
zuqqhi2