Personal guidelines for sh scripting
- Avoid syntax or semantics unique to bash, zsh, or any other specific shell, eg:
- array contructs
- parameter subtitution;
<()or>() {a,b,c}or{1..10}- the
functionkeyword at the beginning of a function - C-like for loops,
for 1)
- Avoid
basename, useexpror substrings (${variable##\*/}) instead - Use
=over== - Use
caseovertestor[for regex - Use
[ortestover[[ - Use
command -voverwhich - Use
awkoversed,grep,cut,sort,trorunique - Use
/bin/shover/bin/bashor/usr/bin/env sh - Use
$(foo)over\foo\`` - Use
$2)over$(expr “${i}” + 1) - Use
:as a sed separator, eg:sed -e 's:foo:bar:' - Use lowercase over uppercase, except in vars users will interact with, eg:
LC_ALL - Use spaces over tabs
- Use braces around variables, eg,
Bad
var="$foo" var="$foo$bar" var="/path/$foo.suffix"
Good
var="${foo}"
var="${foo}${bar}"
var="/path/${foo}.suffix"
- Define functions with an underscore prefix, eg,
Bad
encode64()
{
steps
}
Good
_encode64()
{
steps
}
- Prefer minimal style:
Bad
if foo
then
bar
fi
if [ -z "${foo}" ]; then
cmd
fi
if [ -z "${foo}" ]; then
cmd
else
other_cmd
fi
_function()
{
steps
}
Good
if foo; then
bar
fi
[ -z "${foo}" ] && cmd
[ -z "${foo}" ] && cmd || other_cmd
_function() {
steps
}
- Local variables should be named after their function name and separated by doble underscore, avoid
local
Bad
_foo()
{
local first_argument="${1}"
}
Good
_foo()
{
_foo__first_argument="${1}"
}
- Use quotes when assigning values to variables, use single quotes when absolutely necesary
Bad
foo=bar
bar=${foo}
Good
foo="bar"
bar="${foo}"
- Use
printfoverecho(specially when echoing ${vars})
Bad
echo "${foo}"
Good
printf "%s\\n" "${foo}"
- Avoid fixed paths in commands:
Bad
[ -f /usr/bin/ps ] && /usr/bin/ps
Good
if command -v "ps" > /dev/null; then
$(command -v "ps")
fi
Better
[ -f "$(command -v "ps")" ] && $(command -v "ps")
- Avoid -q in grep, send stdout to /dev/null instead
Bad
ls | grep -qs file && return 0
Good
ls | grep file >/dev/null && return 0
- Do NOT write to the file system, use vars or pipes instead
Bad
ls > /tmp/ls.output cat /tmp/ls.output && rm /tmp/ls.output
Good
ls_output="$(ls)"
printf "%s\\n" "${ls_output}"
Better
printf "%s\\n" "$(ls)"
- On string comparations use X as a prefix
Bad
[ "${cmd}" = "foo" ] && cmd
Good
[ X"${cmd}" = X"foo" ] && cmd
- Use
||and&&over-aand-o
Bad
if [ "-d" = "${1}" -o "--delete" = "${1}" ]; then
foo
fi
Good
if [ X"-d" = X"${1}" ] || [ X"--delete" = "${1}" ]; then
foo
fi