Linux Admin Basics 2 of 3 – shell scripting

Bash Options

You can set bash option in two ways, with shopt or with set command. For example:

shopt -s extglob
set -o nounset

Here is a list of options. I often use “set -e” right after shebang to tell the script to exit upon failed command, because the rest of the script will be error-prone after the failed command. I can use “set +e” to negate the flag.

Also, we can use the unset command to delete the variables during program execution, or the export command to export a variable or function to the environment of all the child processes running in the current shell.

User menu and argument processing

It is a common task in bash scripting to display user menu or process argument. Both use case structure. Here are the examples:

User menu:

#! /bin/bash
select car in BMW TOYOTA TESLA
do
   case $car in
   BMW)
      echo "X3";;
   TOYOTA)
      echo "Camry";;
   TESLA)
      echo "Some";;
   *)
      echo "Unknown";;
   esac
done

Argument processiong example:

#! /usr/bin/bash
while getopts ":ht" opt; do
  case ${opt} in
    h ) echo "option h"
      ;;
    t ) echo "option t"
      ;;
    \? ) echo "Usage: cmd [-h] [-t]"
      ;;
  esac
done

Try to run the script with the following options:

$ test.sh -t
$ test.sh -h
$ test.sh -ht
$ test.sh -th
$ test.sh -a

Build-in commands declare, local, let, eval

The declare command allow you to assign an attribute to a variable. For example:

  • -a indexed array
  • -A associative array
  • -i integer
  • -r read only

The local descriptor declare a variable that is only effective in the code block. For example, within the function.

With command let, each arg is taken as arithmetic expression:

$ A=1
$ B=2
$ let sum=$A+$B
$ echo "A=$A, B=$B, C=$C"
$ A=1, B=2, C=3

With command eval, each arg is taken as a string:

$ A=1
$ B=2
$ eval sum=$A+$B
$ echo "A=$A, B=$B, C=$C"
A=1, B=2, C=1+2

Commands trap and inotifywait

The trap command that you can use to catch signals from script and execute code. For example:

trap "info 'caught interrupt, will stop'; exit 2" INT

Inotify is a file system monitoring mechansim. The inotifywait can be used in many complex scenarios. It requires inotify-tools package. As an example, the following snippet use inotifywait to montor a directory, and trigger rsync upon changes

#!/bin/bash

DESTHOST=172.17.23.132
DESTHOSTDIR=/www/htdocs/
SRCDIR=/www/htdocs/

inotifywait -mr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' \
-e create,delete,modify,attrib $SRCDIR | while read DATE TIME DIR FILE; do
   $FILECHANGE=${DIR}${FILE}
   rsync -avze 'ssh' $SRCDIR root@${DESTHOST}:${DESTHOSTDIR} &>/dev/null && \
   echo "At ${TIME} on ${DATE}, file $FILECHANGE was backed up via rsync" >> /var/log/filesync.log
done

The example above shows how to copy a file to a remote server upon change. There is a simpler alternative to it, a tool called lsyncd, which combines rsync and inotify. lsyncd is a linux package (can be installed with yum) and remains an open source project.

The configuration is done in LUA language. Here is a simple example:

settings {
    logfile    = "/var/log/lsyncd/lsyncd.log",
    statusFile = "/var/log/lsyncd/lsyncd.stat",
    statusInterval = 1
}

sync{
    default.rsyncssh,
    source="/etc/dhunch/",
    host="remote-host",
    targetdir="/etc/dhunch/",
    delay = 10,
    exclude={'*.bak',
             '*.tmp',
             'deploy.log',
             'themes/'},
    rsync={
       checksum=true,
       times=true,
       chown="hunch:hunch",
       chmod="755"
    }
}

In this example, we tell lsyncd to use rsyncssh mechanism and it can use hosts defined in openssh configuration (e.g. ~/.ssh/config). For more than one hosts, we can declare the sync section more than once. Full documentation is here.