======================== Bash and shell scripting ======================== Shellcheck ========== This utility (shell linter) is installed on PMSN. **Use it without moderation**. You can also `use it online `_. .. code-block:: bash 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): .. code-block:: bash set -euo pipefail * Trace each command verbosely: .. code-block:: bash set -x String replace ============== * replace ``home`` with ``scratch`` from ``$SGE_O_WORKDIR``, save into ``$SCRATCHDIR``: .. code-block:: bash SCRATCHDIR=${SGE_O_WORKDIR/"home"/"scratch"} * name ``$OUTFILE`` and ``$LOGFILE``, following ``$INFILE`` (change file extension): .. code-block:: bash INFILE=$1 TMPNAME=$(basename "${INFILE%.*}") OUTFILE="$TMPNAME"-done.hdf5 LOGFILE="$TMPNAME".log * Explicit error message in case of unset variable .. code-block:: bash # ${TMPDIR:?"TMPDIR nonexistent"} result=$(ls "${TMPDIR:?"TMPDIR nonexistent"}") if [[ "$result" == "nonexistent" ]] then echo "TMPDIR do not exist!" exit 1 fi Pattern matching ================ .. code-block:: bash #!/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: .. code-block:: bash #1 nope #2 matched #3 matched To get #1 matching, remove quotes around RegExpr, or set compat31 : ``shopt -s compat31`` .. code-block:: bash #!/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: .. code-block:: bash yep, it matches at the start Associatives arrays =================== We can name array entries (aka named index). * tips : .. code-block:: bash 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: .. code-block:: bash for i in "${tab[@]}"; do echo "${i}"; done * tip (induce loop): .. code-block:: bash printf "%s\n" "${tab[@]}"; done Resulting in code simplification (and readability): * Array traversal, knowing the index (here ``$value``): * before : .. code-block:: bash 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: .. code-block:: bash 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!!**