2015-11-12 00:37:43 +00:00
Coding style used for my bash projects (v2.1 Oct 2015)
2015-09-08 14:08:14 +00:00
2015-11-12 00:37:43 +00:00
++++++ Header
2015-09-09 07:38:05 +00:00
Always use the following header
2015-11-12 00:37:43 +00:00
2015-09-09 07:38:05 +00:00
----BEGIN HEADER
#!/usr/bin/env bash
PROGRAM="program-name" # Long description
AUTHOR="(L) 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
2015-11-12 00:37:43 +00:00
#!/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
2015-09-09 07:38:05 +00:00
2015-11-12 00:37:43 +00:00
type instead of type -p for bash test (other shells don't know -p)
++++++ Indentation
2015-09-08 14:08:14 +00:00
Using tabs
Transform old shell scripts using unexpand command
2015-11-12 00:37:43 +00:00
++++++ Comments
2015-09-08 14:08:14 +00:00
2015-09-10 19:42:11 +00:00
Some command # comment
## Some comment on a new line
2015-09-08 14:08:14 +00:00
################################################# Some separation
2015-11-12 00:37:43 +00:00
++++++ Work comments
2015-09-09 07:38:05 +00:00
2015-11-12 00:37:43 +00:00
Whenever there is some idea to postpone, use #TODO[-version]:[dev-name:] some remark
2015-09-10 19:42:11 +00:00
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
2015-11-12 00:37:43 +00:00
Example: #TODO-v2.1:deajan: need to do something
2015-09-09 07:38:05 +00:00
2015-11-12 00:37:43 +00:00
++++++ Variables
2015-09-09 07:38:05 +00:00
All local variables are lowercase, separated by _ (ex: low_wait)
All global variables full upercase, separated by _ (ex: EXEC_TIME)
2015-09-10 19:42:11 +00:00
All environment variables (verbose, silent, debug, etc) have prefix _ and are full upercase, separated by _ (ex: _PARANOIA_DEBUG)
2015-09-09 07:38:05 +00:00
2015-11-12 00:37:43 +00:00
++++++ Functions
2015-09-08 14:08:14 +00:00
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 $?
}
2015-11-12 00:37:43 +00:00
++++++ Sub functions
2015-09-08 14:08:14 +00:00
When a function is a subroutine of another function, it is called _SomethingAsSubFunction
2015-11-12 00:37:43 +00:00
++++++ Function argument check
2015-09-09 12:50:21 +00:00
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.
2015-11-12 00:37:43 +00:00
++++++ If statements
2015-09-08 14:08:14 +00:00
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
2015-11-12 00:37:43 +00:00
++++++ Logging
2015-09-08 14:08:14 +00:00
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
2015-11-12 00:37:43 +00:00
++++++ Eval
2015-09-08 14:08:14 +00:00
2015-09-19 16:40:26 +00:00
Most commands should be logged to a tmp file.
The basic way of doing is:
2015-11-12 00:37:43 +00:00
cmd='"something '$somevar'" > some_file 2>&1'
2015-09-19 16:40:26 +00:00
eval $cmd &
2015-11-12 00:37:43 +00:00
WaitForTaskCompletion $! 0 0 $FUNCNAME
2015-09-19 16:40:26 +00:00
Remote commands should exist as:
cmd=$SSH_CMD' "some; commands \"'$VARIABLE'\" some; other; commands" > some_file 2>&1'
2015-11-12 00:37:43 +00:00
++++++ File variables
2015-09-19 16:40:26 +00:00
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.
2015-09-08 14:08:14 +00:00
2015-11-12 00:37:43 +00:00
++++++ 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.