Coding style used for my bash projects (v2.4 Sep 2016)

++++++ Header

Always use the following header

----BEGIN HEADER
#!/usr/bin/env bash

PROGRAM="program-name" # Long description
AUTHOR="(C) 20XX-20YY by Orsiris 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(priority):[dev-name:] some remark
Priority can be critical, high, medium, low, verylow. No release can happen if there are TODOs other than low or verylow.
Example: #TODO(high):deajan: need to do something

A "work in progress" marker must be left on the line a dev is working when it's work isn't finished). Marker is #WIP:dev-name: some remark
dev-name is mandatory if more than one person is coding
Example: #WIP:deajan: missing function something

++++++ Variables

All local variables names have each first letter of the words uppercase and all others lowercase, except for the first word where all letters are lowercase
Example: someLongVariable
All global variables are full upercase, separated by _
Example: EXEC_TIME
All environment variables (verbose, silent, debug, etc) have prefix _ and are full upercase, separated by _
Example: _PARANOIA_DEBUG

Exec time variables that can take boolean values should use true and false instead of 1 and 0.

++++++ Functions

All function names should begin with an uppercase letter for every word, the other letters should be lowercase
Example: SomeFunctionThatRocks

Bash does not provide any checks against missing function arguments. Also, missing quotes can lead to an inconsistent number of arguments.
Most functions should have a first line that calls the special function __CheckArguments, which checks the number of given arguments for a function in order
to find possible problems. Number of arguments are given as first argument to __CheckArguments. May be a number or a range, eg 0-2 if the function takes optional arguments.
__CheckArguments will only trigger when the script is launched with _PARANOIA_DEBUG=yes. Also, it will only exist in the debug version.
Use the following convention for function definition:

function SomeFunction {
	__CheckArguments 0 $# ${FUNCNAME[0]} "$@"               #__WITH_PARANOIA_DEBUG
	...	
}

Use sed ':a;N;$!ba;s/\n{\n/ {\n/g' to convert functions that have opening brackets on a new line.

If the function has arguments, use local variable names that are more readable than $1...$n. Explain via comments what those variables contain if needed.
Declare arguments before launching __CheckArguments:

function AnotherFunction {
	local varName="${1}"
	local otherVarName="${2}" # This variable contains stuff
	__CheckArguments 2 $# ${FUNCNAME[0]} "$@"		#__WITH_PARANOIA_DEBUG
	...
}

Functions should always have return status
function RandomFunction {
	...
	return $?
}

++++++ Sub functions

When a function is a subroutine of another function, it is called _SomethingAsSubFunction:
Example:

function _ApplyLocally
function _ApplyRemotely
function Apply

++++++ For and While statements

For and while statements will have the "do" part on the first line.
Example:

for i in "${var[@]}"; do
	...
done

while [ $i -eq 1 ]; do
	...
done

++++++ 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 instruction 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 variable names, so the separtors apply as wished.

++++++ 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

+++++++ Exit codes

Normal exit code = 0
Run with errors exit code = 1
Run with warnings exit code = 2
Wrong shell exit code = 127
Usage function exit code = 128
