sh Scripting Cheat Sheet
If you’re like me, well, then I feel sorry for you. But if you’re like me, then perl is the first item out of your scripting toolbox. Unfortunately there are moments when you are forced to use sh. And since we’d rather use perl, sh syntax leaves our brains faster than thirteen year old can hack a Windows system. This article provides a brief overview of some of the things we love to forget.
The good news is: sh has a lot of condition operators. The bad news is: sh has a lot of condition operators. Who can remember them all? It helps to have a cheat sheet.
Binary operators
-eq (arg1 is equal to arg2) -ne (arg1 is not equal to arg2) -lt (arg1 is less than arg2) -le (arg1 is less than or equal to arg2) -gt (arg1 is greater than arg2) -ge (arg1 is greater than or equal to arg2)
-z string (length of string is zero) -n string (length of string is non-zero) string1 == string2 (strings are equal) string1 != string2 (string are not equal) string1 < string2 (string1 sorts before string2) string1 > string2 (string1 sorts after string2)
-a file (file exists) -b file (file exists and is a block special) -c file (file exists and is a character special) -d file (file exists and is a directory) -e file (file exists) -f file (file exists and is a regular file) -g file (file exists and is set-group-id) -h file (file exists and is a symbolic link) -k file (file exists and is sticky) -p file (file exists and is a named pipe) -r file (file exists and is readable) -s file (file exists and has a size greater than zero) -t fd (file descriptor is open and refers to a terminal) -u file (file exists and is set-user-id) -w file (file exists and is writable) -x file (file exists and is executable) -O file (file exists and is owned by the current user) -G file (file exists and is owned by the current user group) -L file (file exists and is a symbolic link) -S file (file exists and is a socket) -N file (file exists and has been modified since last read) file1 -nt file2 (file1 is newer by modification date than file2) file1 -ot file2 (file1 is older by modification date than file2) file1 -ef file2 (file1 and file2 have the same device and inode)
Condition Logic
if [ $opt -eq 1 ]; then HOST="www.joedog.org" elif [ $opt -eq 2 ]; then HOST="ftp.joedog.org" else HOST="ssl.joedog.org" fi
Ben $ sh haha haha: line 4: [: -eq: unary operator expected haha: line 6: [: -eq: unary operator expected
if [ ${opt:-0} -eq 1 ] ; then HOST="www.joedog.org" elif [ ${opt:-0} -eq 2 ] ; then HOST="ftp.joedog.org" else HOST="ssl.joedog.org" fi
Logical Boolean Operators
&& - logical AND if [ condition1 ] && [ condition2 ] || - logical OR if [ condition1 ] || [ condition2 ] ! - logical NOT if [ ! condition ]
A=10 B=11 if [ $A -eq 10 ] && [ $B -eq 11 ] ; then echo "Whoo hoo!" fi if [ $A -eq 11 ] || [ $B -eq 11 ] ; then echo "Whoo hoo!" fi if [ ! $A -eq 11 ]; then echo "Whoo hoo!" fi
sh Arrays
Arrays are ordered lists of values. Each value can be accessed by the value’s index in the list. Imagine a key chain, the keys are the values and the chain is the array. Rather than track every key you own, just remember where you put the chain. Where did I put that?
Unfortunately, sh arrays aren’t quite as convenient as a key chain. It is necessary to maintain individual variables but strategic name selection can make access easier. Here’s how we build a sh array:
#!/bin/sh a="Homer Marge Bart Lisa" for CHARACTER in $a ; do echo $CHARACTER doneBen $ sh haha
#!/bin/sh a0="Homer" a1="Marge" a2="Bart" a3="Lisa" # I used standard C array indexing which starts # at zero. I could have just as easily started at # 1 or 1000. # # Now we'll loop through the "array" and access its # values with "eval" i=0 y=4 while [ $i -lt $y ] do eval echo "a${i} = ${a${i}}" let i=$i+1 done
#!/bin/sh SIMPSON[0]="Homer" SIMPSON[1]="Marge" SIMPSON[2]="Bart" SIMPSON[3]="Lisa" SIMPSONS=${SIMPSON[@]} for CHARACTER in $SIMPSONS ; do echo $CHARACTER done
echo ${SIMPSON[1]}
Happy Hacking