If you've not done much Bash development before you may find these debugging tips useful: http://wiki.bash-hackers.org/scripting/debuggingtips
-
Scripts must start with
#!/usr/bin/env bash
. -
Scripts must use
set -e
. if the return value of a command can be ignored, suffix it with|| true
set -e
command_that_might_fail || true
command_that_should_not_fail
Note that ignoring an exit status with || true
is genrally not a good practice
though. Generally speaking it's better to handle the error.
- Scripts should not check exit status via
$?
manually. rely onset -e
instead:
cmd
if [ $? -eq 0 ]; then
echo worked
fi
should be written as:
set -e
if cmd; then
echo worked
fi
- Scripts must include a usage, description and optional examples in this format:
#/ Usage: ghe-this-is-my-script [options] <required_arg>
#/
#/ This is a brief description of the script's purpose.
#/
#/ OPTIONS:
#/ -h | --help Show this message.
#/ -l | --longopt <required_arg> An option.
#/ -c <required_arg> Another option.
#/
#/ EXAMPLES: (optional section but nice to have when not trivial)
#/
#/ This will do foo and bar:
#/ $ ghe-this-is-my-script --longopt foobar -c 2
#/
If there are no options or required arguments, that can be ignored.
-
Customer-facing scripts must accept both
-h
and--help
arguments and print the usage information with anexit 2
status code. -
Scripts should not use Bash arrays.
-
Scripts should use
test
or[
whenever possible:
test -f /etc/passwd
test -f /etc/passwd -a -f /etc/group
if [ "string" = "string" ]; then
true
fi
- Scripts may use
[[
for advanced bash features
if [[ "$(hostname)" = *.iad.github.net ]]; then
true
fi
- Scripts may use bash for loops
for ((n=0; n<10; n++)); do
done
or
for i in $(seq 0 9); do
done
- Scripts should use
$[x+y*z]
for mathematical expressions
local n=1
let n++
n=$[n+1] # preferred
n=$[$n+1]
n=$((n+1))
n=$(($n+1))
- Scripts should use variables sparingly. Short paths and other constants should be repeated liberally throughout code since they can be search/replaced easily if they ever change.
DATA_DB_PATH=/data/user/db
mkdir -p $DATA_DB_PATH
rsync $DATA_DB_PATH remote:$DATA_DB_PATH
vs the much more readable:
mkdir -p /data/user/db
rsync /data/user/db remote:/data/user/db
- Scripts should use lowercase variables for locals, and uppercase for variables inherited or exported via the environment:
#!/bin/bash
#/ Usage: [DEBUG=0] process_repo <nwo>
nwo=$1
[ -n $DEBUG ] && echo "** processing $nwo" >&2
export GIT_DIR=/data/repos/$nwo.git
git rev-list
- Scripts should use
${var}
for interpolation only when required:
greeting=hello
echo $greeting
echo ${greeting}world
- Scripts should use functions sparingly, opting for small/simple/sequential scripts instead whenever possible when defining functions, use the following style:
my_function() {
local arg1=$1
[ -n $arg1 ] || return
...
}
- Scripts should use
<<heredocs
when dealing with multi-line strings:
<<eof
and<< eof
will allow interpolation<<"eof"
and<<'eof'
will disallow interpolation<<-eof
and<<-"eof"
will strip off leading tabs first
cat <<"eof" | ssh $remote -- bash
foo=bar
echo $foo # interpolated on remote side after ssh
eof
bar=baz
cat <<eof | ssh $remote -- bash
echo $bar > /etc/foo # interpolated before ssh
chmod 0600 /etc/foo
eof
- Scripts should quote variables that could reasonably have a space now or in the future:
if [ ! -z "$packages" ]; then
true
fi
- All tests should use
set -e
before making any assertions:
begin_test "echo works"
(
set -e
echo passing | grep passing
)
end_test
- If you want to assert failure, please resist the urge to disable
set -e
and instead use negation with!
:
begin_test "netcat is not from bsd"
(
set -e
setup
! nc -h 2>&1 | grep bsd
)
end_test