While Bash is very popular (and so is its syntax), Dash replaced Bash as non-interactive shell in 2011 with the release of Debian 6 (Squeeze). Dash is POSIX-compliant, small and fast. For the interactive shell Bash is still used – which is absolutely fine.
However, Bash is not POSIX-compliant which can be a portability problem, especially when shared with the open-source community. Instead of memorizing different syntaxes and features between different shells – which can be dangerously confusing – I decided to use exactly one shell. Since Dash is POSIX-compliant, the choice was clear and I stopped writing shell scripts in Bash.
All of the code above is POSIX-compliant. No language based special features are used ("bashism").
Debian Almquist Shell (dash) ‹ Almquist Shell (ash) ‹ System V Shell Release 4 (SVR4) ‹ Bourne Shell (sh)
See Chapter 2. Shell Command Language of the The Open Group Base Specifications (Issue 7, Edition 2018).
A redirection instruction from standard output (STDOUT = file descriptor 1) or error output (STDERR = file descriptor 2) of a command can be placed either at the end or at the beginning to improve readabilty, so these both are equivalent:
foobar 1>/dev/null1>/dev/null foobarThe instruction >/dev/null means: Redirect standard output (STDOUT) to /dev/null which is a pseudo-device file which just discards the input like a black hole.
For STDOUT you can simply omit the 1, so this is equivalent to 1>/dev/null foobar:
foobar >/dev/nullIf you only want to suppress errors, you would replace the 1 with a 2. This would redirect the error output from the command to /dev/null:
foobar 2>/dev/nullTo redirect both STDOUT and STDERR you would need to combine them with an additional instruction (2>&1):
foobar >/dev/null 2>&1The 2>&1 here means: Redirect STDERR to STDOUT. Since STDOUT is redirected to /dev/null, both are suppressed.
If you want to redirect something to STDERR, e.g. an error message in your script, you can use >&2 and place it at the beginning for improved readabilty:
>&2 printf '%s\n' "This is an error message."The >&2 here is equivalent to 1>&2.
Bear in mind that return does not return a boolean, but an exit code which is a positive integer between 0 and 255. While 0 stands for success, any other value indicates an error:
# Check if string is an unsigned integer (also +1 is not considered as be valid)
func_IS_UNSIGNED_INTEGER() {
NUMBER="$1"
case "$NUMBER" in
*[!0-9]* | '')
# False
return 1
;;
esac
# True
return 0
}You should use printf instead of echo, because it is more portable:
echo "Text with a new line after it."printf '%s\n' "Text with a new line after it."- https://askubuntu.com/questions/467747/which-is-better-printf-or-echo
- https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo
In the most cases, you want to place double-quotes ($VAR) around variables. This also applies to command substitution. Otherwise not all of the content of the variable – including whitespace – is passed, but the variable gets splitted.
RESULT="$(dpkg-query -s "$PACKAGE_NAME" >/dev/null 2>&1)"RESULT=$(dpkg-query -s "$PACKAGE_NAME" >/dev/null 2>&1)RESULT=$(dpkg-query -s $PACKAGE_NAME >/dev/null 2>&1)While backticks (`) – or also called backquotes – are not officially deprecated, their use is discouraged. Instead use $(...).
RESULT="`dpkg-query -s "$PACKAGE_NAME"` >/dev/null 2>&1"- https://unix.stackexchange.com/questions/126927/have-backticks-i-e-cmd-in-sh-shells-been-deprecated
- https://unix.stackexchange.com/questions/118433/quoting-within-command-substitution-in-bash
# Single line comment: '''
Comment
over
multiple
lines.
'''VAR="This string is 34 characters long."
printf '%s\n' "${#VAR}"VAR="/foo/bar/"
printf '%s\n' "${VAR#/}"VAR="/foo/bar/"
printf '%s\n' "${VAR%/}"- https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
Let's say you have a configuration file with following content:
# ID Name Country
01 Morton US
02 Matthew CAOne way to assign these variables would be using awk '{print $...}':
PARAM_ID="$(printf '%s' "$LINE" | awk '{print $1}')"
PARAM_NAME="$(printf '%s' "$LINE" | awk '{print $2}')"
PARAM_COUNTRY="$(printf '%s' "$LINE" | awk '{print $3}')"However, this will create one subshell for each parameter. What you could also do is following:
read -r \
PARAM_ID \
PARAM_NAME \
PARAM_COUNTRY \
__TRAILING \
<<-EOF
$(printf '%s' "$LINE")
EOFThe variable __TRAILING at the end covers the rest of the line in case it has more words then expected.