3: Using the builtins : and true and parameter expansion ------------------------------------------- Shell has an interesting and often underutilized NOOP feature, the colon ':' or shell builtin 'true'. There's a natural affinity between the NOOP and parameter expansion's side effects. NOOP can also be used to ignore (casually) some errors. Allow subsequent commands and expansions with side effects ========================================================== The NOOP (:) and the true builtin allow for arguments which are ignored. Since each command exits zero, either one can be followed with expansion tests. Parameter expansion allows very simple "test and set" functionality in shell. For example, this is a fairly common bit of shell to write: test -z "$APP_VAR1" && APP_VAR1=default That's pretty fast, since "test" is a shell builtin and shortcut logic means that the variable assignment will never run if APP_VAR1 contains a value. This is also pretty readable. But, here's a fun use of the NOOP and parameter expansion set and test, which is a functional equivalent of the above: : ${APP_VAR1:=default} Or, perhaps you'd prefer this second functional equivalent: true ${APP_VAR1:=default} Personally, I like my truths unadorned. Now, let's put together a miniature application which can accept its operating parameters as environment variables or as command line arguments, but will abort the program if the user did not specify one or the other: #! /bin/bash # # -- noop-show; using NOOP, user must specify APP_VAR* vars : ${APP_VAR0:="$1"} # -- sets APP_VAR0="$1" if $APP_VAR0 empty : ${APP_VAR1:="$2"} : ${APP_VAR2:="$3"} for var in ${!APP_VAR*} ; do : ${!var:?$var not set, supply envar or arguments.} printf "%s=%s\n" "$var" "${!var}" done This side effect is very convenient in the above case, because by the time we enter the 'for' loop, the variables APP_VAR0, APP_VAR1 and APP_VAR2 each exist, even if each one only contains "". $ ./noop-show ./noop-show: line 11: !var: APP_VAR0 not set, supply envar or arguments. $ ./noop-show a b c APP_VAR0=a APP_VAR1=b APP_VAR2=c $ ./noop-show a b APP_VAR0=a APP_VAR1=b ./noop-show: line 11: !var: APP_VAR2 not set, supply envar or arguments. $ export APP_VAR0=q APP_VAR1=r APP_VAR2=s $ ./noop-show APP_VAR0=q APP_VAR1=r APP_VAR2=s In a real shell application or script, the above-provided error messages would probably not be adequate. And, for the record, the logic is probably backward above. Normally, users, rather sanely, expect the command line arguments to take precedence over any environment variables. The above example script reverses this logical expectation. Guarantee that a function or command exits zero =============================================== Consider that you have a shell function and you want to make sure that it always returns zero. The following function would (or could) succeed if the user has sufficient privilege. If the user does not have sufficient privilege, the function would return the error returned by "wc". unknown_exit () { wc -l /proc/kcore } By adding the NOOP, you can, prevent the function from returning a non-zero exit code. Note that this is quite similar to using the shell builtin "true". using NOOP using true ------------------- ------------------- safe_exit () { safe_exit () { wc -l /proc/kcore wc -l /proc/kcore : true } } This is much more commonly done when running a command whose exit code, success or output doesn't matter to the rest of the script. Assume that the script "should" sleep for some specified period of time, and it doesn't really matter if somebody kills the sleep before the time is up (causing sleep to exit non-zero). sleep $NAP_TIME || : So, let's say that you have a little shell script in a loop fetching your RSS feeds, and the $NAP_TIME between fetches is 1800 seconds (half an hour). Now, all of a sudden, you are impatient, you trawl through the process table, find the sleep between RSS feed updates and kill it. Now, the sleep will exit non-zero, but with the || :, any non-zero exit of the sleep call will call your NOOP.