Classic Shell Scripting - Arnold Robbins [202]
Example 14-1. Saving shell state, indcluding functions, for bash and ksh93
{
set +o Option settings
(shopt -p) 2>/dev/null bash-specific options, subshell silences ksh
set Variables and values
export -p Exported variables
readonly -p Read-only variables
trap Trap settings
typeset -f Function definitions (not POSIX)
} > /tmp/shell.state
Note that bash and ksh93 can use different syntaxes for defining functions, so care is required if you wish to dump the state from one shell and restore it in the other!
echo is not portable
As described in Section 2.5.3, the echo command may only be used portably for the simplest of uses, and various options and/or escape sequences may or may not be available (the POSIX standard notwithstanding).
In ksh93, the built-in version of echo attempts to emulate whatever external version of echo would be found in $PATH. The reason behind this is compatibility: on any given Unix system, when the Korn shell executes a Bourne shell script for that system, it should behave identically to the original Bourne shell.
In bash, on the other hand, the built-in version behaves the same across Unix systems. The rationale is consistency: a bash script should behave the same, no matter what Unix variant it's running on. Thus, for complete portability, echo should be avoided, and printf is still the best bet.
OPTIND can be a local variable
In Section 6.4.4, we described the getopts command and the OPTIND and OPTARGS variables. ksh93 gives functions defined with the function keyword a local copy of OPTIND. The idea is that functions can be much more like separate scripts, using getopts to process their arguments in the same way a script does, without affecting the parent's option processing.
${ var :? message } may not exit
The ${ variable :? message } variable expansion checks if variable is set. If it isn't, the shell prints message and exits. However, when the shell is interactive, the behavior varies, since it's not always correct for an interactive shell to just blindly exit, possibly logging the user out. Given the following script, named x.sh:
echo ${somevar:?somevar is not set}
echo still running
bash and ksh93 show the behaviors listed in Table 14-1.
Table 14-1. Interactivity of ${var:?message} in bash and ksh93
Command
Message printed
Subsequent command run
$ bash x.sh
Yes
No
$ ksh93 x.sh
Yes
No
bash$ . x.sh
Yes
Yes
ksh93$ . x.sh
Yes
No
This implies that if you know that a script will be executed with the dot command, you should ensure that it exits after using the ${ variable :? message } construct.
Missing loop items in a for loop
Here's a subtle point. Consider a loop such as:
for i in $a $b $c
do
do something
done
If all three variables are empty, there are no values to loop over, so the shell silently does nothing. It's as if the loop had been written:
for i in # nothing!
do
do something
done
However, for most versions of the Bourne shell, actually writing a for loop that way would produce a syntax error. The 2001 POSIX standard made an empty loop valid when entered directly.
The current versions of both ksh93 and bash accept an empty for loop as just shown, and silently do nothing. As this is a recent feature, older versions of both shells, as well as the original Bourne shell, are likely to produce an error message.
DEBUG traps behave differently
Both ksh88 and ksh93 provide a special DEBUG trap for shell debugging and tracing. In ksh88, the traps on DEBUG happen after each command is executed. In ksh93, the DEBUG trap happens before each command. So far so good. More confusing is that earlier versions of bash follow the ksh88 behavior, whereas the current versions follow that of ksh93. This is illustrated in Section 13.3.2.
Long and short options