1 Shell scripts and batch-files

The LED-version Planck-constant lab is very intensive in data analysis, requiring that you make three graphs per LED, 21 graphs, plus the final frequency versus V min plot. Shell scripts or batch files are used for massively repetitive file analysis, or conversion, or production. In a shell script you use the login shell of the computer as a programmable interpreter.
All shell scripts begin with the line

#!/bin/sh

and are made executable by (saving them with a name such as “script.sh”) and running

chmod a+x script.sh

on them. The syntax of a bash shell script is rather particular, but is quite like C syntax. The best way to learn scripting is by example.

Example. Nuclear counting and frequency plot

Suppose that in the nuclear counting experiment you create a data file in which each line is the number of counts in a ten second interval. To sort this into a frequency plot of the number of occurances of each count we use a script containing an AWK routine, and pipes of the output into the uniq, sort, and graph commands. If your MinGW or MSYS installation does not have sort or uniq, you can get a coretils package from http://gnuwin32.sourceforge.net/ that contain both. Read the man pages for command line switches and instructions.

#!/bin/sh  
# reads in a file of numbers and sorts x, 0<= x<= 11  
# them into a frequency plot  
# and graphs it  
awk ’{  
    i=0  
    while ( i<11 ) {  
    if ( $1 ==i )  
    print i  
    ++i}  
    }’ $1 | sort -n | uniq -c | awk ’{print $2, " ", $1}’ | graph -T X

Print filtering

The script below is an automatic conversion utility that will accept any file of types .c, .cc, .h, .f, .txt, .tex, .pdf, .ps, .gif, .jpg, .html and will perform the conversion into postscript automatically, and print the converted file. You can use it by simply typing

Print filename

of course giving it the correct filename for the file you wish to print. The script illustrates how to program the shell to accept input that is switched on using cases. The script invokes a number of common UNIX text programs such as file, ps2pdf, enscript, lp and others.

#!/bin/sh  
# file type detection and print filter  
 
for i in .ps .dvi .f .cc .c .h .tex .gif .jpg .html .gz .tgz .bz2 .pdf  
do  
base=$(basename $1 $i)  
if [ $1 = $base$i ]  
then  
# determine file type  
type=$i  
fi  
done  
 
# now do something  
case "$type" in  
    ".ps" )  
    echo $1 is a postscript file  
    echo Printing  
    lp $1  
    ;;  
    ".dvi")  
    echo $1 is a dvi file  
    echo Converting to postscript with dvips  
    base=$(basename $1 .dvi)  
    dvips -o ${base}.ps $1  
    echo Printing and removing the postscript file  
    # lp ${base}.ps  
    # rm ${base}.ps  
    ;;  
    ".tex")  
    echo $1 is a tex file, converting to postscript  
    base=$(basename $1 .tex)  
    enscript -p ${base}.ps $1  
    echo Printing and removing the postscript file  
    # lp ${base}.ps  
    # rm ${base}.ps  
    ;;  
    ".pdf")  
    echo Ahh, a PDF file, converting to postscript and printing  
    base=$(basename $1 .pdf)  
    pdf2ps $1 ${base}.ps  
    # lp ${base}.ps  
    # rm ${base}.ps  
    ;;  
    ".html")  
    echo Open this file with Mozilla, print it to  
    echo a postscript file, and print that  
    ;;  
    ".gz" | ".tgz" | ".bz2" )  
    echo This is a compressed archive  
    ;;  
    ".c")  
    echo $1 is a C source file  
    echo Converting to postscript  
    enscript -p $1.ps $1  
    echo Printing and removing the postscript file  
    # lp $1.ps  
    # rm $1.ps  
    ;;  
    ".cc")  
    echo $1 is a C++ source file  
    echo Converting to postscript  
    enscript -p $1.ps $1  
    echo Printing and removing the postscript file  
    # lp $1.ps  
    # rm $1.ps  
    ;;  
    ".f")  
    echo $1 is a FORTRAN77 source file  
    echo Converting to postscript  
    enscript -p $1.ps $1  
    echo Printing and removing the postscript file  
    # lp $1.ps  
    # rm $1.ps  
    ;;  
    ".h")  
    echo $1 is a C header  file  
    echo Converting to postscript  
    enscript -p $1.ps $1  
    echo Printing and removing the postscript file  
    # lp $1.ps  
    # rm $1.ps  
    ;;  
    * )  
    echo I dont know what this shit is  
    echo Sorry, I dont know what to do with it  
    ;;  
esac

This shell script illustrates how the shell bash can actually be programmed, or rather used as an interpreter. Each line of the script is a command interpreted by bash, and the shell understands loop, conditionals, cases, and integer arithmetic. This shell script invokes the commands echo, basename, dvips, enscript, lp, convert, rm and dvips. In other words a script is a collection of commands that you would normally type into the shell and execute one by one, the script simply automates them.

Bulk file conversion or processing

Below is a short example script that converts all .fig files in your working directory into .gif images, with the color white being made transparent.

#!/bin/bash  
#shell script for fig --> gif conversion with white transparency  
 for file in $(ls *.fig)  
 do  
 echo $file  
 base=$(basename $file .fig)  
 echo $base  
 convert $file $base.ppm  
 /usr/local/netpbm/ppmtogif -trans "#FFFFFF" $base.ppm > $base.gif  
 done  
 exit 0  

Makefile creation

The script below will accept a command-line option, your system type, and create a makefile for compiling all C-sources in your working directory.

#!/bin/sh  
# Uncle Jeff’s handy Makefile generator script for multi-file projects  
echo \# Makefile for Graphics programs  
echo \# you may need to adjust the FLAGS or CFLAGS variable  
echo \# depending on whether you are building g2 or libplot programs  
printf " \n\n"  
 
type=$1  
case "$type" in  
"win" | "Win" | "w" | "W" | "win32" | "evil")  
echo SUFFIX = ".exe"  
SUFFIX=".exe"  
echo CPATH = /usr/local/cross-tools/bin/  
echo LPATH = /usr/local/cross-tools/lib/  
echo LPATH2 = /usr/local/cross-tools/i386-mingw32msvc/lib/  
echo IPATH = /usr/local/cross-tools/include/  
echo IPATH2 = /usr/local/cross-tools/i386-mingw32msvc/include/  
echo ’CC = $(CPATH)/i386-mingw32msvc-gcc’  
echo ’GCC = $(CPATH)/i386-mingw32msvc-gcc’  
echo ’CXX = $(CPATH)/i386-mingw32msvc-g++’  
echo ’G77 = $(CPATH)/i386-mingw32msvc-g77’  
echo ’CPP = $(CPATH)/i386-mingw32msvc-cpp’  
echo ’LD = $(CPATH)/i386-mingw32msvc-ld’  
echo ’AR = $(CPATH)/i386-mingw32msvc-ar’  
echo ’AS = $(CPATH)/i386-mingw32msvc-as’  
echo FLAGS = -O2 -fomit-frame-pointer -static  
echo CFLAGS = -static -O2 -DPFD_DOUBLEBUFFER=1 -DTBSTYLE_EX_DOUBLEBUFFER=1  -DUNIX=1 -DX_DISPLAY_MISSING=1 -DDO_WIN32=1 -DHAVE_LIMITS_H=1 -DWIN32 -DPDCURSES -D__MINGW32__  
 
echo ’LDFLAGS = -I$(IPATH) -I$(IPATH2) -L$(LPATH) -L$(LPATH2) -lpdcurses -lg2 -lm -lkernel32 -lgdi32 -lmsvcrt -luser32 -lcomdlg32’  
echo GMPFLAGS =  -lpdcurses -lm -lgmp  
echo LDXFLAGS =  -lpdcurses -lplot -lpng -lz -lm  
;;  
 
"linux" | "Linux" | "l" | "L" | "unix" | "good" | "mingw" | "Mingw")  
echo CC = /usr/bin/gcc  
echo CPP = /usr/bin/cpp  
echo \# Change suffix to .exe for mingw  
echo SUFFIX = ""  
case "$type" in  
"mingw" | "Mingw")  
SUFFIX=".exe"  
echo CFLAGS = -static -O2 -DPFD_DOUBLEBUFFER=1 -DWIN32 -DDO_WIN32=1  
echo \# uncomment one of these;  
echo \# mingw LDFLAGS for libplot programs  
echo \#LDFLAGS = -lplot -lpng -lz -lm  
echo \# mingw LDFLAGS for g2 programs  
echo \#LDFLAGS = -lg2 -lm -lkernel32 -lgdi32 -lmsvcrt -luser32 -lcomdlg32  
;;  
"linux" | "Linux" | "l" | "L" | "unix")  
SUFFIX=""  
echo CFLAGS = -DXWIN -O2 -fomit-frame-pointer  
echo LDFLAGS = -L/usr/X11R6/lib -I/usr/X11R6/include -lplot -lXaw -lXmu \\  
printf "\t-lXt -lSM  -lICE -lXext -lX11 -lm -lcurses -lgd -lg2\n"  
;;  
esac  
 
 
;;  
*)  
echo "Not a valid target"  
echo "pick from win, Win, win32, Linux, unix, linux, mingw, Mingw"  
exit 0  
;;  
esac  
 
printf "\n\n# targets\n\n"  
printf "all: \t"  
for file in $(ls *.c); do  
base=$(basename $file .c)  
printf "%s " "$base$SUFFIX"  
done  
printf "\n\n"  
 
# targets  
for file in $(ls *.c); do  
base=$(basename $file .c)  
printf "%s\n" "$base$SUFFIX: $file"  
printf "\t%s\n" "\$(CC) \$(CFLAGS) $file \$(LDFLAGS) -o $base$SUFFIX"  
printf "\n"  
done  

Data file and bulk graphing

The LED Planck law can be easily analyzed by saving the I versus V data for each LED under a filename, for example use the LED wavelength in nanometers as the filename, such as “586.raw”. Write a program that reads this in, and creates several related files, such as
586.diff, a table of ddIV- versus I,
586.diff.line, two points on the line that best fits the data in 586.diff
586.theory, points on the curve I = I0(eα-1(V -V min) - 1) versus V that best fits the data in 586.raw.
and so forth. Do this for all sevel LEDs, and the script below will create all 21 graphs in one shot.

#!/bin/bash  
 
 for file in $(ls *.raw)  
 do  
 base=$(basename $file .raw)  
 
# create all of the analyzed data files  
 ./analysis $base  
#create all of the graphs  
graph -T ps $base.theory -L "$base (raw data), best fit curve" -m 0 -S 4 -X "V (Volts)" -Y"I (mA)" $file > $base.1.ps  
graph -T ps $base.log.line -L "ln(I/I\sb0\eb+1) versus V for $base" -m 0 -S 4 -X "V (Volts)" -Y "ln(I/I\sb0\eb+1)" $base.line > $base.2.ps  
graph -T ps $base.diff.line -L "dI/dV versus I for $base" -m 0 -S 4 -X "I (mA)" -Y "dI/dV, (mA/V)"  $base.diff > $base.3.ps  
 done  
 exit 0