Rush
Rush

Reputation: 31

Bash emulation of try/catch not working as expected

The following switch statement has strange behavior:

3)
        if [ $state -ne $last_state ]
        then
            echo "state: $state"
        fi

        stty -F $SERIAL $BAUD -echo igncr
        echo "befor cat"
        {
            cat -v $SERIAL >> $USBDRIVE/gpsData_$filecounter.txt && echo "after cat"
        } || {
            echo "catch"
            state=0
            last_state=3
            ((filecounter++))
        }           
        ;;

I thought, that when the cat command fails while is beeing excecuted, "after cat" will be written and than the part after || will be excecuted. But when I look at the output, it seems that after echoing "after cat" a break happen so that the actual state does not change and will be entered one more time. Then stty fails, too (becaus the serial adapter is missing). After that, the cat command againt fails at the beginning but now enters the "catch" block....

Here is the relevant output:

pi@rpi ~/serial_logger $ ./serial_logger.sh
serial adapter found: ttyUSB0
state: 1
USB-Storage found: usb0
state: 3
before cat
after cat    #here should be entered state 0
state: 3
stty: /dev/ttyUSB0: No such file or directory
before cat
cat: /dev/ttyUSB0: No such file or directory
catch
state: 0
USB-Storage found: usb0
state: 2

What am I doing wrong?

Upvotes: 3

Views: 545

Answers (1)

Michael Jaros
Michael Jaros

Reputation: 4681

Why it doesn't work

In your example, the cat succeeds which is why "after cat" is printed and the "catch group" is not entered.

I thought, that when the cat command fails while is beeing excecuted, "after cat" will be written

This assumption is wrong, "after cat" will be written only if cat succeeds because you used the && operator.

and than the part after || will be excecuted.

Note that there is no such thing as a "groupfail" which would allow you to actually emulate try/catch behaviour in Bash using command groups ({ [...] }). Consider this example with multiple lines inside your group:

{
  cmd1
  cmd2
} || { # catch block

As long as cmd2 succeeds, your program will not enter the catch block, even if cmd1 fails.

How to emulate try/catch

You could however use the && operator to emulate try/catch behavior:

cmd1 && cmd2 && cmd3 || { # catch block

This could also be written as

{ cmd1 && cmd2 && cmd3 ; } || { # catch block

You see that however you do it, the syntax is a little clumsy.

Maybe this related stackoverflow question can help you too.

Other error handling mechanisms

Last, but not least, two common Bash error handling mechanisms should be mentioned:

  • Registering a signal handler for the ERR signal causes Bash to call a function whenever a command returns a non-zero exit status:

    trap error_handler ERR
    
    function error_handler() {
      echo error
    }
    
  • Setting the errexit shell option causes your program to terminate as soon as a command returns a non-zero exit status (signal handlers are still executed):

    set -e
    

Upvotes: 2

Related Questions