===== 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 ''function'' keyword at the beginning of a function
* C-like for loops, ''for ((i=0; i<3; i++))''
* Avoid ''basename'', use ''expr'' or substrings (${variable##\*/}) instead
* Use ''='' over ''==''
* Use ''case'' over ''test'' or ''['' for regex
* Use ''['' or ''test'' over ''[[''
* Use ''command -v'' over ''which''
* Use ''awk'' over ''sed'', ''grep'', ''cut'', ''sort'', ''tr'' or ''unique''
* Use ''/bin/sh'' over ''/bin/bash'' or ''/usr/bin/env sh''
* Use ''$(foo)'' over ''\''foo\``
* Use ''$((${i}+1))'' 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 ''printf'' over ''echo'' (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 ''-a'' and ''-o''
**Bad**
if [ "-d" = "${1}" -o "--delete" = "${1}" ]; then
foo
fi
**Good**
if [ X"-d" = X"${1}" ] || [ X"--delete" = "${1}" ]; then
foo
fi