Just simple methods to keep the code clean.
Inspired by progrium/bashstyle and Kfir Lavi post.
- All code goes in a function
- Always double quote variables
- Avoid global variables and declare them as
readonly - Always have a
main()function for runnable scripts - Always use
set -eo pipefail: fail fast and be aware of exit codes - Define functions as
myfunc() { ... }, notfunction myfun {...} - Always use
[[instead of[ortest - Use
$( ... )instead of backticks - Prefer absolute paths and always qualify relative paths with
./. - Warnings and errors should go to
STDERR, anything parsable should go toSTDOUT - Use
.shor.bashextension if file is meant to be included or sourced
- Avoid global vars
- Always UPPER_CASE naming
- Readonly declaration
- Globals that can be always use in any program :
readonly PROGNAME=$(basename $0)
readonly PROGDIR=$(readlink -m $(dirname $0))
readonly ARGS="$@"
- All variables should be local (they can only be used in functions)
- Always lowercase naming
- Self documenting parameters
fn_example() {
local explicit_name = $1 ;
local expName = $1 ;
}
- Usually use
ifor loop, so it is very important to declare it as local
- Use always a
main()function - The only global command in the code is :
mainormain "$@" - If script is also usable as library, call it using
[[ "$0" == "$BASH_SOURCE" ]] && main "$@"
- Only the
main()function and global declarations are run globaly - Short code portion can be functions
- Define functions as
myfunc() { ... }, notfunction myfun {...}
- Run with -x flag :
bash -x prog.sh - Debug just a small section of code using set -x and set +x
- Printing function name and its arguments
echo $FUNCNAME $@
- Break expression with back slash
\ - Use symbols at the start of the indented line
print_dir_if_not_empty() {
local dir=$1
is_empty $dir \
&& echo "dir is empty" \
|| echo "dir=$dir"
}
cmdline() {
local arg=
for arg
do
local delim=""
case "$arg" in
#translate --gnu-long-options to -g (short options)
--config) args="${args}-c ";;
--pretend) args="${args}-n ";;
--test) args="${args}-t ";;
--help-config) usage_config && exit 0;;
--help) args="${args}-h ";;
--verbose) args="${args}-v ";;
--debug) args="${args}-x ";;
#pass through anything else
*) [[ "${arg:0:1}" == "-" ]] || delim="\""
args="${args}${delim}${arg}${delim} ";;
esac
done
#Reset the positional parameters to the short options
eval set -- $args
while getopts "nvhxt:c:" OPTION
do
case $OPTION in
v)
readonly VERBOSE=1
;;
h)
usage
exit 0
;;
x)
readonly DEBUG='-x'
set -x
;;
t)
RUN_TESTS=$OPTARG
verbose VINFO "Running tests"
;;
c)
readonly CONFIG_FILE=$OPTARG
;;
n)
readonly PRETEND=1
;;
esac
done
if [[ $recursive_testing || -z $RUN_TESTS ]]; then
[[ ! -f $CONFIG_FILE ]] \
&& eexit "You must provide --config file"
fi
return 0
}
-
Very important in higher level languages
-
Use shunit2 for unit testing
-
Good intro to shunit2 : shUnit2 - Bash Testing
-
Another good ressource : Test Driving Shell Scripts
-
The list of current assertions (as of version 2.1.6) :
assertEquals [message] expected actualassertSame [message] expected actualassertNotEquals [message] expected actualassertNotSame [message] expected actualassertNull [message] value# used to compare a null in bash which is a zero length stringassertNotNull [message] value# used to compare a null in bash which is a zero length stringassertTrue [message] conditionassertFalse [message] condition
-
The list of current failures (do not use them for value comparisons, use assertions for this) :
fail [message]failNotEquals [message] unexpected actualfailSame [message] expected actualfailNotSame [message] unexpected actual
-
More specific functions :
setUp: run automatically before each testtearDownrun automatically after each test|| startSkippingautomatically skip after a test failure (default is to continue)
- Obsolete and deprecated bash syntax : http://wiki.bash-hackers.org/scripting/obsolete
- Beginner mistakes http://wiki.bash-hackers.org/scripting/newbie_traps
- Advanced Bash-Scripting Guide http://tldp.org/LDP/abs/html/
- Google's Bash styleguide http://google-styleguide.googlecode.com/svn/trunk/shell.xml