Shell Scripting Tips

  • Giving default value to script arguments:
   SERVER=${1:-default.server.org}
   PORT=${2:-8888}
  • alternative to 'find * | grep':
   # enable globstar (**)
   shopt -s globstar

   # find all py files somewhere under .
   ls **/*.py
  • print last word of each line:
    cat /some/file | /bin/awk '{print $NF}' 
  • a better skelleton script:
 #!/bin/bash

 ARGS=$@
 TEST='false'
 PKGS=
 DOPUPPET='true'

 usage() {
    cat << EOF 
 USAGE: $0 [--no-puppet] --packages foo,bar
 Install or update some packages after optionaly running the puppet agent.
 EOF
 }

 parse_args() {
    while [ "$1" != "" ]; do
        case $1 in
            "--packages")     PKGS=$2;; shift;;
            "--no-puppet")    DOPUPPET='false';;
            "--test")         TEST='true';;
            "--debug")        set -x; DEBUG='true';;
            "-h" | "--help")  usage; exit 0;;
            *)                echo "Unknown argument '$1' (-h for help)"; exit 0;;
        esac
        shift
    done
 }

 parse_args $ARGS
  • Skelleton usage and optargs:
    #!/bin/bash

    set -e

    usage() {
        cat << EOF
    usage: $0 [options]

    Do blablabla

    OPTIONS:
      -h      this help
      ...

    Example:
    $ $0 ...
    EOF
    }

    while getopts "r:s:h" OPT; do
        case $OPT in
        r) FOO=$OPTARG;;
        s) BAR=$OPTARG;;
        h) usage; exit 0;;
        *) usage; exit 1;;
        esac
    done

    shift `expr $OPTIND - 1`

    ARGS=$@

    ...
  • Exit on error:
    set -e
  • Accept errors for just one command:
    set -e
    # do stuff
    set +e; command_that_missbehaves; set -e
  • Turn on trace:
    set -x
  • Redirect script output to a file:
    exec >> file.log 2>&1
  • Conditionals:
    if [ -z "$DEBUG" ] || [ "$DEBUG" -eq 0 ];

    [ $debug -eq 1 ] && {
        ...
    }

    test -f  $TF &&         test '!' -z "`find $x -newer $TF -print`" &&
        cp $x $TF;
  • One line to multiple line (in a pipe):
    echo "bob is good" | while read A B C; do
        echo "$A";
        echo "$B";
        echo "$C";
    done
  • Multiple lines to one line:
    cat /this/long/file | xargs
  • As first line of a library:
    #!/bin/echo WARNING: this library should be sourced!
  • Default value of variable:
    [ "$VAR" ] || VAR="default value"
  • Nice redirections:
    {
        echo "this"
        echo "that"
        cat somefile
    } > file.out

    cat file.in | while read line; do
        echo "got $line"
    done >> file.out
  • Increment:
    i=$((i+1))
  • Redirecting output with sudo:
    echo "this and that" | sudo tee /file/owned/by/root
  • Using install:
    /usr/bin/install -c -m 644 file1 file2 /dest/dir

    # similar to mkdir -d
    /usr/bin/install -c -d /new/dir1 /new/dir2
  • Template logging library:
    # Set to 1 to see debug info. Set to 0 to hide it
    DEBUG=0

    MYNAME='scriptname'

    # Basic logging to /var/log/daemon
    loginfo() {
        logger -p daemon.info -i -s "$MYNAME INFO: $1"
    }

    logerror() {
        logger -p daemon.crit -i -s "$MYNAME ERROR: $1" && exit 1
    }

    logdebug() {
        if [ ! -z "$DEBUG" -a "$DEBUG" == "1" ]; then
            logger -p daemon.debug -i -s "$MYNAME DEBUG: $1"
        fi
    }
  • Piping to /var/log:
    ps auxwwf | logger -p auth.debug -i -s
  • Finding out ip and mac (dirty hack):
    case `uname` in
        Linux)
            # Select only the 1st public IP
            MYIP=$( $IFCONFIG | grep 'inet addr:' | grep -v '127.0.0.1' | head -n 1| cut -d: -f2 | awk '{print $1}' )
            MYMAC=$( $IFCONFIG | grep -B 1 "$MYIP" | grep HWaddr | sed -e "s/.*HWaddr //" -e "s/ *//g" )
            ;;
        *)   logerror "Cannot identify IP and MAC on this operating system";;
    esac
  • Searching for packages available in /etc/apt/sources.list and matching a name:
    $ apt-get update
    $ apt-cache search bob
    $ apt-cache show qa-ta-runner
  • Indent a command's output:
    $ cat index.html | sed -e "s/^/    /"
  • Using screen:
    $ screen -ls     # list current screens
    $ screen -S bob  # create a screen named 'bob'
    $ screen -x bob  # attach to 'bob'
    ctl-a d          # detach from 'bob'
  • Cut to filter heading fields:
    DOMAIN=$(echo $HOSTNAME | cut -d . -f 2-)