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 referencetrap 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.
Error handling check.$ ./main.sh [INFO]Main [INFO]Finish
Add error command to main function and try to run it.
Result ist following.#!/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
$ ./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.Let’s check error handling part with following program which has an error.#!/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
Result is following.#!/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
$ ./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
main.sh result is following.#!/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"
test.sh result is following.$ ./main.sh [INFO]Main [INFO]Lib Main [INFO]Finish
[INFO]Setup testLibMain [INFO]Lib Main Ran 1 test. OK