Getting Bashed By Bash

There are a number of popular command shells that run on Linux, MacOS (from MacOS X the MacIntosh operating system is based on Mach, which is itself a derivative of BSD UNIX), UNIX and CygWin (a UNIX command environment that runs on Windows). These include bash, which is probably the most popular shell on Linux, my personal favorite tcsh (which is based on an earlier shell csh) and ksh. Each of these shells has it's own scripting language. Shell scripts can be quite complex and are sometimes used to install software or perform other maintenance tasks.

In what seems to have become a famous (and widely quoted) attack on the tcsh/csh scripting language, Csh Programming Considered Harmful, Tom Christiansen writes

Resolved: The csh is a tool utterly inadequate for programming, and its use for such purposes should be strictly banned.

I am continually shocked and dismayed to see people write test cases, install scripts, and other random hackery using the csh. Lack of proficiency in the Bourne shell has been known to cause errors in /etc/rc and .cronrc files, which is a problem, because you must write these files in that language.

Mr. Christiansen goes on to describe what he believes to be the various faults of csh scripts. I found his section titled Stupid parsing bugs particularly amusing:

White space can matter:


    if(expr)

may fail on some versions of csh, while


    if (expr)

works!

What is amusing about this is that spaces also matter in bash (and I assume in ksh/sh scripts). For example


    a = `pwd`  # an incorrect assignment

is not the same as


    a=`pwd`  # a correct assignment

A similar problem exists with bash if statements


    if [-n $some_string]  # spaces must follow and preceed the square brackets
    then
      echo $some_string "is not null"
    fi


    if [ -n $some_string ]  # this one is correct
    then
      echo $some_string "is not null"
    fi

What Mr. Christiansen misses in this essay is that all of the shell scripting languages suck. They are poorly designed, even for their era, their parsing is pathetic and they are frustrating to learn and use. The ghastly character of shell scripting languages prompted the original development of the Perl scripting language (Mr. Christiansen is also famous for his work on Perl).

Although shell scripting languages suck, they can be useful for simple maintenance tasks. A shell script is always guaranteed to run on a UNIX like system, which must support bash/sh in order to function (although it's worth noting that Perl is installed by default on most UNIX like systems these days too).

This web page, and the rant on shell scripts was promted by my desire for a shell script that would copy files that differed from a USB thumb drive (key chain disk) to my working software directory. Although I've written many shell scripts over the course of my career, I don't write them often. So when I find a need for a shell script, I find that I've forgotten the syntax and the quirks of the scripting language. As a result, it took over an hour to write the silly script.

Along with the working shell script, one of the useful results of this frustrating exercise was that I found a very good reference on Bash scripts:

The author of the Bash scripting guide, Mendel Cooper, not only endeared himself to me for this excellent guide to Bash but for his essay A Shrug for Atlas: A short essay on the propaganda novel along with a lot of other wonderful content on his web site The Eclectic Pastiche

After all this blather, here, for my future reference and your edification and enjoyment I've included my shell script below:

#!/bin/bash
# 
# Copy Java files and ANTLR grammer files that have changed from one
# directory to the other.  Note that the destination directory should
# be a root directory that mirrors the directory the script is
executed
# in.
# 

if [ ! -n "$1" ]
then
   echo "Usage: $0 "
   exit 1
fi

dst_dir=$1

echo "destination directory is $dst_dir"
echo "current directory is " `pwd`

java_files=`find . -name "*.java" -o -name "*.g"`

cnt=0
for i in $java_files
do
   rslt=`diff -q -b $i $dst_dir$i`
   diff_status=$?
   if [ $diff_status -eq 1 ]
   then 
     echo "copying $i to $dst_dir$i"
     cp $i $dst_dir$i
     (( cnt++ ))
   fi
done

if [ $cnt -eq 0 ]
then
  echo "There were no differences, so no files were copied"
fi

exit 0


back to Notes on Software and Software Engineering