This article describes the impact of using the lesser discussed alternative code block terminators ;;& and ;& which control the „match-resuming“ behavior of Bash’s case…esac.
The following example shows a quite common use of Bash’s case…esac:
#!/bin/bash
# A simple option parser, understanding two "flag" type options.
for argument in "$@" ; do
case "$argument" in
--option-1)
echo "Doing something specific for option --option-1 ..."
;;
--option-2)
echo "Doing something specific for option --option-2 ..."
;;
*)
echo "Saw any other argument besides the known options: \"$argument\"."
;;
esac
done
In each of the matching blocks within the case statement, the character sequence ;; causes the program flow to terminate the block and to leave the case statement:
--option-1)
echo "Doing something specific for option --option-1 ..."
;;
In natural words: „If the value of shell variable argument is exactly --option-1 then execute the echo command and exit the case statement.“
For the next example, the option behavior shown above will be extended as follows:
- If a parameter value starts with --option-* then do something that all such options have in common, and
- match and perform specific actions for --option-1 and --option-2.
This can be accomplished by using ;;& instead of ;; as the terminator of the match code block, which will cause the code block to not exit case, but instead resume pattern matching with the remaining patterns:
--option-*)
echo "Doing something that all --option-* options have in common (saw \"$argument\") ..."
;;& # resume matching attempts with all patterns below
--option-1)
echo "Doing something specific for --option-1 ..."
;;
--option-2)
echo "Doing something specific for --option-2 ..."
;;
In natural words:
- If the value of argument matches --option-* then execute the first echo, then proceed to match argument against the following patterns.
- If the value of argument matches --option-1 then execute the second echo, then exit case.
- If the value of argument matches --option-2 then execute the third echo, then exit case.
The option behavior will again be extended for the next example:
- If a parameter value starts with --option-* then do something that all such options have in common,
- match and perform specific actions for --option-1 and --option-2,
- match and perform specific actions for --feature-1, and
- if --option-1 was specified, the specific actions for option --feature-1 should be performed after the specific actions for --option-1.
This can be accomplished by putting the pattern-match block for --feature-1 immediately after the block for --option-1 and using ;& instead of ;; when terminating the block for --option-1:
--option-*)
echo "Doing something that all --option-* options have in common (saw \"$argument\") ..."
;;&
--option-1)
echo "Doing something specific for --option-1 ..."
;& # also execute the code from the matching block below
--feature-1)
echo "Doing something specific for --feature-1 ..."
;;
--option-2)
echo "Doing something specific for --option-2 ..."
;;
The final example considers the return value that is produced by such a case…esac construct. Note that case…esac does not really have a return value by itself – it is a „program flow“ construct – and the return value of the last executed command will be the value of special variable $? after the case…esac has been processed.
#!/bin/bash
# A parser of several options, for demonstration purposes.
for argument in "$@" ; do
case "$argument" in
--option-*)
echo "Doing something that all --option-* options have in common (saw \"$argument\") ..."
;;&
--option-1)
echo "Doing something specific for --option-1 ..."
;&
--feature-1)
echo "Doing something specific for --feature-1 ..."
false # intentionally produce a false return value
;;
--option-2)
echo "Doing something specific for --option-2 ..."
;;
*)
echo "Saw any other argument besides the known options: \"$argument\"."
;;
esac
rv=$?
echo "Done processing argument \"$argument\"; return value of \"case...esac was $rv"
done
Try out this script with various parameters and observe the return value produced by each iteration of for. What options cause the return value to be 1?
- Bourne to Bourne Again Shell Forward Compatibility
- Print XDG Desktop Definition for Application
- Using sed or awk to ensure a specific last Line in a Text
- Make a Bourne Again Shell Script Log its Output to a File
- Maintaining Multi-Line „stat“ Formats using Bourne Again Shell
- Print all indented Lines following a non-indented Line
- An Introduction to Programmable Completion in Bash
- Match-Resumption in case…esac of Bourne Again Shell