sh Scripting Cheat Sheet

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.

Condition Operators

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)
String operators
  -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)
File operators
  -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 you’ve gotten this far, you’ve either mastered English or  Google Translate. “If, then” is a common English logic construct. If he looks like a douche and he talks like a douche, then he’s probably a douche. Most computer languages skip the superfluous “then.” Not sh! If you skip it, your program will explode in several million pieces. (Or not, but whatever).
Here’s a sh conditional:
if [ $opt -eq 1 ]; then
  HOST="www.joedog.org"
elif [ $opt -eq 2 ]; then
  HOST="ftp.joedog.org"
else
  HOST="ssl.joedog.org"
fi
Your author tested that logic and it works like a charm. But what happens if opt is unitialized? Let’s say we want to assign a value  at run time: opt=$1 # $0 is the program’s name, $1 is the first arg. If a user runs your script without providing an argument, your program is gonna explode is several million pieces. (Or not, …)
Ben $ sh haha  
haha: line 4: [: -eq: unary operator expected
haha: line 6: [: -eq: unary operator expected
It’s pretty hard to evaluate nothing. We can avoid this problem by ensuring $opt evaluates to something. It’s best set it explicitly, but we can also provide it with a default value. Check this out:
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

Do the business people in your company have exceptions for every rule? “Do this, no that, except when, but if…” If that’s the case, you’re gonna need yourself some boolean operators.
  && - logical AND  if [ condition1 ] && [ condition2 ]
  || - logical OR   if [ condition1 ] || [ condition2 ]
  !  - logical NOT  if [ ! condition ]
What? You want more explicit examples? Sheesh.
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
Ben $ sh papa
Whoo hoo!
Whoo hoo!
Whoo hoo!

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
done
Ben $ sh haha
Homer
Marge
Bart
Lisa
That was convenient. Unfortunately, we can’t reference each value by its index. The perl motto applies to sh as well: “There’s more than one way to do it!” Or something like that. Here’s an array implementation with index references:
#!/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
Ben $ sh haha
a0 = Homer
a1 = Marge
a2 = Bart
a3 = Lisa
I know what you’re saying, “meh!” Fortunately, there are alternatives to this kludge. If you’re scripting on Linux, your sh is actually bash which has much better support for arrays. Let’s bashify this script, shall we?
 
#!/bin/sh 
SIMPSON[0]="Homer"
SIMPSON[1]="Marge"
SIMPSON[2]="Bart"
SIMPSON[3]="Lisa"

SIMPSONS=${SIMPSON[@]}
for CHARACTER in $SIMPSONS ; do
  echo $CHARACTER
done
Ben $ sh haha
Homer
Marge
Bart
Lisa
At the risk of insulting your intelligence, you can reference Marge in this manner:

echo ${SIMPSON[1]}

Happy Hacking

Posted in | 4 Comments

4 Responses to “sh Scripting Cheat Sheet”

  1. zXuS says:

    Awesome, Thanks! =D

  2. llz says:

    fantastic!

  3. At the risk of insulting your intelligence, you can reference Marge in this manner:
    echo ${SIMPSON[1]}

    FTFY

  4. sh Scripting is very hard to uderstand , not maybe learn VB.NET ?

Leave a Reply




Recent Comments

  • league of legends program: Hello I am so excited I found your site, I really found you by error, while I was looking...
  • Sohan: I am using node-js http server. I created http request to hit the server and log the message. In that case...
  • Jeff Fulmer: I don’t know what “simple http server” means. If you’re using apache out of the...
  • Sohan: MY server is simple http server. Can you tell me what thresol paramter or property is used in siegerc file.
  • Jeff Fulmer: Ninety-five percent of the time, this error occurs because you’ve configured more simulated users...