Start bash files with ``` #!/usr/bin/env bash ``` Declare global variables at the beginning of the script. Include a short comment to add context to the variables. ``` # name of BUP file generated by Nvidia script BUPFILE="${BUPFILE:-bl_update_payload}" ``` Create variables that can be overridden ``` # ORIG_DIR: starting directory where the script will drop the user on exit ORIG_DIR="${ORIG_DIR:-$(pwd)}" ``` Properly configured variables can be overridden at execution. For example ``` wp@host:~/$ ORIG_DIR="My override value" ./my_script.sh ``` Allow easy debugging early in the script. The following code block will enable easy debugging, which will print each line of code before it is executed ``` if [ ! -z ${DEV+x} ] && [ "$DEV" -eq "1" ]; then # enable early debugging with `DEV=1 build.sh` set -x PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' else set -eu fi ``` The script can be debugged by prefixing the script with the correct parameter assignment. ``` wp@host:~/$ DEV=1 ./my_script.sh ``` Group functions together under the following banner ``` #====================================================================================================================== #===== FUNCTIONS ===== #====================================================================================================================== ``` Create a header for each function using the following template ``` #--- FUNCTION ------------------------------------------------------------------------------------------------------- # NAME: __wget # DESCRIPTION: Download if file doesn't exist. # USAGE: __wget [outfile] [url] # NOTES: if [outfile] exists, it will be tested to verify it is a regular file #---------------------------------------------------------------------------------------------------------------------- __wget() { # do stuff } ``` For large, complex scripts, create a banner marking the start of execution, such as ``` #====================================================================================================================== #===== BEGIN ===== #====================================================================================================================== ``` Use sub-banners if needed ``` #---------------------------------------------------------------------------------------------------------------------- #----- Command Line Args ----- #---------------------------------------------------------------------------------------------------------------------- ``` Include a useful help ``` #--- FUNCTION ------------------------------------------------------------------------------------------------------- # NAME: __usage # DESCRIPTION: Display program help # USAGE: __usage #---------------------------------------------------------------------------------------------------------------------- __usage() { cat << EOT Usage: ${__SCRIPTNAME} [options] [action]... Actions: - dtb Build Devicetree binary files - blob Build Mass Flash tool to flash Nano over USB - bup Build Bootloader Update Package - tpm Build the TPM kernel modules - tpm-rng Build the TPM hardware random number generator module - pkg Build the update package to distribute to Nano - dev Enable development (debug) mode. Mainly, disables cleanup of temporary files created during program execution - all Same as ${__SCRIPTNAME} dtb bup tpm pkg blob Does not build the optional tpm-rng module - help Display usage Options: -h Display this help -D Enable debugging mode Examples: - ${__SCRIPTNAME} all - ${__SCRIPTNAME} all dev - ${__SCRIPTNAME} dtb tpm bup EOT } ``` Common bash test expressions https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html and https://stackoverflow.com/a/21164441 ``` The following test operators return "True" if the file exists -a file :: Any file type -e file :: Any file type -f file :: Any regular file (ie not a block, socket, pipe, symlink, etc.) -b file :: Block special file -c file :: Character file -d file :: Directory type -h file :: Symbolic link -L file :: Symbolic link -p file :: Named pipe file (FIFO) -s file :: Socket file The following operators test existing files for specific properties -g file :: Any file AND set-group-id bit set -k file :: Any file AND sticky bit set -r file :: Any file AND file is readable -w file :: Any file AND file is writable -x file :: Any file AND executable bit is set -s file :: Any file AND file size is greater than 0 Other common test operators -v varname :: True if 'varname' is set and has been assigned a value -R varname :: True if varname is set and is a name reference -n string :: True if length of string is not zero string1 == string2 :: True if the strings are equal string1 = string2 :: True if the strings are equal. POSIX conformant string1 != string2 :: True if the strings are not equal string1 < string2 :: True if string1 sorts before string2 string1 > string2 :: True if string1 sorts after string2 Arthimetic test operators arg1 -eq arg2 :: True if arg1 is equal to arg2 arg1 -ne arg2 :: True if arg1 is not equal to arg2 arg1 -lt arg2 :: True if arg1 is less than arg2 arg1 -le arg2 :: True if arg1 is less than or equal to arg2 arg1 -gt arg2 :: True if arg1 is greater than arg2 arg1 -ge arg2 :: True if arg1 is greater than or equal to arg2 ```