Coding style used for my bash projects (v2.1 Oct 2015) ++++++ Header Always use the following header ----BEGIN HEADER #!/usr/bin/env bash PROGRAM="program-name" # Long description AUTHOR="(C) 20XX-20YY by Orsiris \"Ozy\" de Jong" CONTACT="http://www.example.com me@example.com" PROGRAM_BUILD=YYYYMMDDVV ## Optional instructions ----END HEADER Using bind style versionning: YYYYMMDDVV (Year, Month, Day, Revision): Example: 2015012402 = 2nd revision of 24 Jan 2015 #!/usr/bin/env bash instead of #!/bin/bash Change old scripts with for i in $(grep -r '#!/bin/bash' * |cut -f1 -d':'); do sed -i 's&#!/bin/bash&#!/usr/bin/env bash&g' $i; done type instead of type -p for bash test (other shells don't know -p) ++++++ Indentation Using tabs Transform old shell scripts using unexpand command ++++++ Comments Some command # comment ## Some comment on a new line ################################################# Some separation ++++++ Work comments Whenever there is some idea to postpone, use #TODO[-version]:[dev-name:] some remark A marker must be left where on the line a dev is working (when the work isn't finished). Marker is #WIP:dev-name: some remark dev-name is mandatory if more than one person is coding Example: #TODO-v2.1:deajan: need to do something ++++++ Variables All local variables are lowercase, separated by _ (ex: low_wait) All global variables full upercase, separated by _ (ex: EXEC_TIME) All environment variables (verbose, silent, debug, etc) have prefix _ and are full upercase, separated by _ (ex: _PARANOIA_DEBUG) ++++++ Functions Every word in a function begins with an uppercase (ex: SomeFunctionDoesThings) Define functions this way. Use sed ':a;N;$!ba;s/\n{\n/ {\n/g' to adapt when opening bracket is on a new line. function something { } If function has some arguments, use local variable names that are more readable than $1...$n. Explain via comments what those variables contain if needed. function anotherthing { local var_name="${1}" local other_var_name="${2}" # This variable contains stuff } Functions should always have return status function thirdthing { some_command return $? } ++++++ Sub functions When a function is a subroutine of another function, it is called _SomethingAsSubFunction ++++++ Function argument check Bash does not provide any checks against missing function arguments. Also, missing quotes can lead to an inconsistent number of arguments. Every function call will be checked by __CheckArguments which takes the number of arguments, $# (the real number of args given), the parent function name and the parent function's arguments. __CheckArguments will trigger a critical error if number of arguments if incorrect. This will also prevent silent typo errors. Ex: function Something { local some="${1}" local other="${2}" local args="${3}" __CheckArguments 3 $# $FUNCNAME "$*" __CheckArguments will only trigger if script is called with DEBUG=yes Also, with PARANOIA_DEBUG=yes, __CheckArguments will recount all arguments given by "$*" and compare. This can mislead if arguments contain spaces. ++++++ If statements If statements will be fully written (word "if" must be used). then is written on the same line. (Use sed ':a;N;$!ba;s/]\n\t*then/]; then/g' to convert files to this format... Replace "],new line, zero or more tabs, then" by "; then") if [ something ]; then stuff else other stuff fi ++++++ Logging A logging function is available with the following levels of logging: - DEBUG: Only log this when DEBUG flas is set in program. Any command forged for eval should be logged by this. - NOTICE: Standard messages - WARN: Requires attention - ERROR: Program produced an error but continues execution - CRITICAL: Program execution is halted ++++++ Eval Most commands should be logged to a tmp file. The basic way of doing is: cmd='"something '$somevar'" > some_file 2>&1' eval $cmd & WaitForTaskCompletion $! 0 0 $FUNCNAME Remote commands should exist as: cmd=$SSH_CMD' "some; commands \"'$VARIABLE'\" some; other; commands" > some_file 2>&1' ++++++ File variables All eval cmd should exit their content to a file called "$RUNDIR/osync.$FUNCNAME.$SCRIPT_PID" Dots are used instead of '_' so variables can be separated with a forbidden char in variables, so they get detected. ++++++ Finding code errors Use shellcheck.net now and then (ignore SC2086 in our case) Use a low tech approach to find uneven number of quotes per line tr -cd "'\n" < my_bash_file.sh | awk 'length%2==1 {print NR, $0}' tr -cd "\"\n" < my_bash_file.sh | awk 'length%2==1 {print NR, $0}' ++++++ ofunctions As obackup and osync share alot of common functions, ofunctions.sh will host all shared code. Dev programs n_osync.sh and n_obackup.sh will source ofunctions.sh Release programs will still include ofunctions.sh in order to enhance ease of use. Ofunctions are defined like: #__FUNC:FunctionName function FunctionName { } #__ENDFUNC These functions are inserted into code that has placeholders like #__FUNC:FuncName