Bash and shell scripting
Shellcheck
This utility (shell linter) is installed on PMSN. Use it without moderation. You can also use it online.
shellcheck myscript.sh
online version: https://www.shellcheck.net/ or https://explainshell.com/
online explanation for error code
SC2154
(example): https://github.com/koalaman/shellcheck/wiki/SC2154
Debugging
Use “strict mode with pipefail” (exit without continuing at first error):
set -euo pipefail
Trace each command verbosely:
set -x
String replace
replace
home
withscratch
from$SGE_O_WORKDIR
, save into$SCRATCHDIR
:
SCRATCHDIR=${SGE_O_WORKDIR/"home"/"scratch"}
name
$OUTFILE
and$LOGFILE
, following$INFILE
(change file extension):
INFILE=$1
TMPNAME=$(basename "${INFILE%.*}")
OUTFILE="$TMPNAME"-done.hdf5
LOGFILE="$TMPNAME".log
Explicit error message in case of unset variable
# ${TMPDIR:?"TMPDIR nonexistent"}
result=$(ls "${TMPDIR:?"TMPDIR nonexistent"}")
if [[ "$result" == "nonexistent" ]]
then
echo "TMPDIR do not exist!"
exit 1
fi
Pattern matching
#!/bin/bash
#shopt -s compat31
VARIABLE="Let's test this string!"
# This is for regular expressions:
# See https://www.shellcheck.net/wiki/SC2076
if [[ "$VARIABLE" =~ "Let's.*ring" ]]
then
echo "#1 matched"
else
echo "#1 nope"
fi
# without quotes
if [[ "$VARIABLE" =~ Let\'s.*ring ]]
then
echo "#2 matched"
else
echo "#2 nope"
fi
# And here we have Bash Patterns:
if [[ "$VARIABLE" == L*ing! ]]
then
echo "#3 matched"
else
echo "#3 nope"
fi
Results:
#1 nope
#2 matched
#3 matched
To get #1 matching, remove quotes around RegExpr, or set compat31 : shopt -s compat31
#!/bin/sh
thisString="1 2 3 4 5"
searchString="1 2"
# if you single quote your input, you could do this
# searchString=$1
case $thisString in
# match exact string
"$searchString" ) echo yep, it matches exactly ;;
# match start of string
"$searchString"* ) echo yep, it matches at the start ;;
# match end of string
*"$searchString" ) echo yep, it matches at the end ;;
# searchString can be anywhere in thisString
*"$searchString"* ) echo yep, it matches in the middle somewhere ;;
*) echo nope ;;
esac
Results:
yep, it matches at the start
Associatives arrays
We can name array entries (aka named index).
tips :
tab[wwsi]="whatever/wwsi"
tab[wwoz]="whatever/to/wwoz"
tab[fip]="http://audio.scdn.arkena.com/11016/fip-midfi128.mp3"
unroll an array:
brute force:
for i in "${tab[@]}"; do echo "${i}"; done
tip (induce loop):
printf "%s\n" "${tab[@]}"; done
Resulting in code simplification (and readability):
Array traversal, knowing the index (here
$value
):before :
tindex=$(for i in "${tab[@]}"; do echo "${i}"; done | grep "${value}") for ((i=0; i<${#tab[@]}; i++)) do if [[ "$tindex" == "{tab[$i]}" ]] then data="${tab[$i]}" fi done
with induce loop:
tindex=$(printf "%s\n" "${tab[@]}" | grep "${value}") data="${tab[$tindex]}"
Warning
Not all these examples have been tested against recent bash and/or shelcheck. DO IT!!