Bash Tips
Here is a list of useful scripting (mainly bash oriented) tricks. For a really good source of bash info check out the Advanced Bash Scripting Guide.
Script variables
Bash has some useful built in variables. Here is a table of a few.
Variable | Description |
---|---|
$$ | The PID of the script |
$! | The PID of the command that has just been executed (and run in the background) |
$? | The exit status of the last command |
$# | The number of command line arguments passed to script |
Command Checker
It's generally a good idea to check that a script has access to all the commands it uses before it starts doing its stuff. The code below can be used to do that. It loops through an array of command names and checks they are available using the type command.
The type
command is used to check if the command is in the path.
The &>
is used to redirect the output of type
to
/dev/null
so that it is not displayed on the terminal.
# Test that required commands are in the path for CMD in rsync awk grep mount df; do type $CMD &> /dev/null if [ $? != "0" ]; then echo "The command '$CMD' is required and is not in your path" exit 1 fi done
Finding Symbolic Link Target
These simple commands can be used to find the target of a symbolic link. The first part simply gets a listing of the file, including the target file. The second part is used to strip away the file part of the string, leaving the target part.
Note: This fails if the link name contains the characters '-> ' (hyphen, greater-than, space).
FILE_NAME=/file/somewhere LS_OUT=$(ls -l "$FILE_NAME") TARGET=${LS_OUT#*-> }
Another, simpler solution, suggested by Paul Donohue, is to use the readlink
command. This seems to be a much
more robust approach.
> readlink -f /path/to/a/symlink
Colourised Output Control
Colour really can make the output of a script easier to read. Here's some helpful bits to achieve that. The -e flag on the echo command makes it interpret the escaped characters properly.
ESC_SEQ="\x1b[" COL_RESET=$ESC_SEQ"39;49;00m" COL_RED=$ESC_SEQ"31;01m" COL_GREEN=$ESC_SEQ"32;01m" COL_YELLOW=$ESC_SEQ"33;01m" COL_BLUE=$ESC_SEQ"34;01m" COL_MAGENTA=$ESC_SEQ"35;01m" COL_CYAN=$ESC_SEQ"36;01m" echo -e $COL_BLUE"INFO: "$COL_RESET"This is an info message" echo -e $COL_RED"An error has occured"$COL_RESET
Force Non-Concurrent Execution
So, you've got a script that you only want a single instance of running at a time? This might help.
The clever bit is to get a lock file test and creation (if needed) to be atomic, that is done without interruption. The set -C stops a redirection from over writing a file. The : > touches a file. In combination, the effect is, when the lock file exists, the redirection fails and exits with an error. If it does not exist, the redirection creates the lock file and exits without an error.
The final part is to make sure that the lock file is cleaned up. To makes sure it is removed even
if the script is terminated with a ctrl-c
, a trap is used. Simply, when the script exits, the trap is run
and the lock file is deleted.
LOCK_FILE=/tmp/.lock (set -C; : > $LOCK_FILE) 2> /dev/null if [ $? != "0" ]; then echo "Lock File exists - exiting" exit 1 fi trap 'rm $LOCK_FILE' EXIT # Do useful stuff
Mount Test
This example is useful for testing if a mount-point is mounted. The output of
mount is fed into awk
which is used to filter out all but text in
the third column (that's where the info on mount-points is held). Then, if a
line that contains the mount-point is found, it is printed. This can then be
used with a normal bash test.
It can also be tweaked to test if a device is mounted, just changed the test column from 3 to 1.
MNT_POINT=/backup MNT_INFO=$(mount | awk -v mnt=$MNT_POINT '{ if ($3 == mnt) print $0 }') if [ "$MNT_INFO" == "" ]; then echo "Not Mounted" else echo "Mounted" fi
Root Check
Sometime it is useful to determine if a script is being run as root or not. A simple check can be performed at the start of a script before anything important is done. The bit of code below will do this.
# Check the script is being run by root if [ "$(id -u)" != "0" ]; then echo "This script must be run as root" exit 1 fi
# Check the script is not being run by root if [ "$(id -u)" == "0" ]; then echo "This script must not be run as root" exit 1 fi