If you've not done much Bash development before you may find these debugging tips useful: http://wiki.bash-hackers.org/scripting/debuggingtips.
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 not a good practice though. Generally speaking, it's better to handle the error.
Rely on set -e
instead:
cmd
if [ $? -eq 0 ]; then
echo worked
fi
should be written as:
set -e
if cmd; then
echo worked
fi
Use this format:
#!/usr/bin/env bash
#/ 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
#/
set -e
If there are no options or required arguments, the OPTIONS
section can be ignored.
They should also print the usage information and exit 2.
For example:
#!/usr/bin/env bash
#/ Usage: ghe-this-is-my-script [options] <required_arg>
#/
#/ This is a brief description of the script's purpose.
set -e
if [ "$1" = "--help" -o "$1" = "-h" ]; then
grep '^#/' <"$0" | cut -c 4-
exit 2
fi
Main issues:
- Portability
- Important bugs in Bash versions < 4.3
test -f /etc/passwd
test -f /etc/passwd -a -f /etc/group
if [ "string" = "string" ]; then
true
fi
if [[ "$(hostname)" = *.iad.github.net ]]; then
true
fi
Preferred:
for i in $(seq 0 9); do
done
or:
for ((n=0; n<10; n++)); do
done
local n=1
let n++
n=$[n+1] # preferred
n=$[$n+1]
n=$((n+1))
n=$(($n+1))
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
versus the much more readable:
mkdir -p /data/user/db
rsync /data/user/db remote:/data/user/db
Use lowercase variables for locals and internal veriables, and uppercase for variables inherited or exported via the environment
#!/usr/bin/env 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
greeting=hello
echo $greeting
echo ${greeting}world
When defining functions, use the following style:
my_function() {
local arg1=$1
[ -n $arg1 ] || return
...
}
<<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
if [ ! -z "$packages" ]; then
true
fi
Use inline comments to disable specific tests, and explain why the test has been disabled.
hexToAscii() {
# shellcheck disable=SC2059 # $1 needs to be interpreted as a formatted string
printf "\x$1"
}
See the style guide