Writing Shell (here: Bash) Scripts for productive systems can be a pain in the ass. Especially if you have varios flavors of linux in your environment.
I’d like to share some tricks I’ve learned in the past few weeks.
Make Bash fussy
This is something you REALLY wanna do:
[cc lang="bash"]#!/bin/bash -e
# or
set -e[/cc]
By either Modifying the Shebang or by using the [cci lang="bash"]set[/cci] command, you set Bash into “Fuzzy Mode”. This causes the script to exit whenever a command fails (and thus, the exit-code ist not 0/true).
You can disable this functionality for individual commands:
[cc lang="bash"]command_that_may_fail || true
# or
set +e
command_that_may_fail1
command_that_may_fail2
set -e[/cc]
Activate X-Ray (debug) Mode
[cc lang="bash"][ -n "$DEBUG" ] && set -x[/cc]
This turns -x on if DEBUG is set to a non-empty String and causes Bash to be VERY talkative.
Example. This is our Script:
[cc lang="bash"]#!/bin/bash -e
[ -n "$DEBUG" ] && set -x
w1=”Hello”
w2=’World’
echo “${w1} `echo $w2 | sed ‘s/World/Universe/’`!”[/cc]
And this is what we get:
[cc lang="bash"]dratir@ajax:~$ DEBUG=1 ./test.sh
+ w1=Hello
+ w2=World
++ echo World
++ sed s/World/Universe/
+ echo ‘Hello Universe!’
Hello Universe![/cc]
Get the absolute Path to the Script
This is basically an easy one, you just have to know it:
[cc lang="bash"]script=”`readlink -f $0`”
wdir=`dirname “$script”`[/cc]
However, if you use [cci lang="bash"]pwd[/cci] in your script prior to this call, it will fail
Check for needed commands
When working with many different installations, it’s a good idea to check if you’ve got everything you need in your Script:
[cc lang="bash"]for cmd in command1 install useradd groupadd command2
do
test -x “`which $cmd`”
done[/cc]
Because of -e (remember to set it), the script dies if a command can’t be found or executed.
Prevent infinite loops (and your Server from crashing)
Sometimes you need your script to restart itself. If you do it wrong [...] you may produce an infinite loop of self calling (which will cause your system to fail after the ~20’000st call, and thus, the ~60’000st open file).
So use this instead:
[cc lang="bash"]restart() {
pscount=`ps aux | grep $0 | wc -l` # count myself
[ $pscount -lt 10 ] || exit 1 # Exit if we are in a Loop
$script
# Don’t forget to fill $script with the absolute path to the script (see above)
exit 0 # to prevent the rest of the script to run
}[/cc]