« Bash Leanings
January 10, 2014 • ☕️ 1 min read
I recently refactored a pretty large and complex set of bash scripts.
I think Ruby influenced my Bash coding style quite a lot.
Here’s a list of some of the patterns I’ve followed.
Have a basedir variable available
script\_base="\`dirname "$0"\`"
Separation of concerns
Write loosely couple bash code in separate files, import them by doing:
. $script\_base/utils/colors.sh
Avoid global state
Limit as much as possible global state (variables), they are evil in any language
export PATH=/usr/pkg/sbin:/usr/pkg/bin:$PATH
Is my one and only export and ‘public’ variable in the script
Separation of concerns
Write independent, small functions
\# Utility function to retrive configuration properties, uses jqfunction get\_config {local environment=$1 local config=$2jq -r ".$environment.$config" config.json}
Documentation
Document your functions, sadly Bash doesn’t allow named params, so it’s pretty hard to figure out what a function takes.
Scope
Leverage local scope: Use local variables
Principle Of Least Astonishment
Check error codes often and offer meaningful log messages
\# Checks the return code of the last command run and outputs error / exit if not nilfunction check\_error {if [ $? -ne 0 ];thenerror "[ERR] $1"exit 1fi}
User interaction
Use colours, Bash is fun with colours
function error {echo "$(tty -s && tput setaf 1)$1$(tty -s && tput sgr0)"}function ok {echo "$(tty -s && tput setaf 2)$1$(tty -s && tput sgr0)"}function warn {echo "$(tty -s && tput setaf 3)$1$(tty -s && tput sgr0)"}
Defensive coding
Check error codes properly also when you run remote scripts
\# Runs a command on remote sshfunction ssh\_exec {local remote\_user=$1local remote\_command=$2local resultsresults=$(ssh -T -q "$remote\_user" "$remote\_command")if [ $? -ne 0 ];thenlog "remote code execution return: $?"log "remote code execution output: $results"error "[ERR] Failed running remote command: $remote\_command"exit 1fi}
In Conclusion
Don’t underestimate Bash, it’s an awesome, Turing Complete language and requires no installation.
When the code starts to get too nasty, use your favourite scripting language!