===== 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