UNIX Shell Script
UNIX Shell Script
Introduction to Shell 3
Comments 3
Variables 4
Expressions in variables 4
Quoting 15
Functions 17
Command substitution 19
Script Examples 20
Debugging 24
Reference 32
Introduction
Saranya 2
Wipro Technologies
How to Write Shell Script
Shell Programming
Even though there are various graphical interfaces available for Linux the shell still is a
very neat tool. The shell is not just a collection of commands but a really good
programming language. You can automate a lot of tasks with it, the shell is very good for
system administration tasks, you can very quickly try out if your ideas work which makes
it very useful for simple prototyping and it is very useful for small utilities that perform
some relatively simple tasks where efficiency is less important than ease of configuration,
maintenance and portability.
So let's see now how it works:
Creating a script
There are a lot of different shells available for Linux but usually the bash (bourne again
shell) is used for shell programming as it is available for free and is easy to use. So all the
scripts we will write in this article use the bash (but will most of the time also run with its
older sister, the bourne shell).
For writing our shell programs we use any kind of text editor, e.g. nedit, kedit, emacs,
vi...as with other programming languages.
The program must start with the following line (it must be the first line in the file):
#!/bin/sh
The #! characters tell the system that the first argument that follows on the line is the
program to be used to execute this file. In this case /bin/sh is shell we use.
When you have written your script and saved it you have to make it executable to be able
to use it.
To make a script executable type
chmod +x filename
Then you can start your script by typing: ./filename
Comments
Comments in shell programming start with # and go until the end of the line. We really
recommend you to use comments. If you have comments and you don't use a certain
script for some time you will still know immediately what it is doing and how it works.
Saranya 3
Wipro Technologies
How to Write Shell Script
Variables
varname=value
To get the value back you just put a dollar sign in front of the variable:
#!/bin/sh
# assign a value:
a="hello world"
# now print the content of "a":
echo "A is:"
echo $a
Type this lines into your text editor and save it e.g. as first. Then make the script
executable by typing chmod +x first in the shell and then start it by typing ./first
The script will just print:
A is:
hello world
Sometimes it is possible to confuse variable names with the rest of the text:
num=2
echo "this is the $numnd"
This will not print "this is the 2nd" but "this is the " because the shell searches for a
variable called numnd which has no value. To tell the shell that we mean the variable
num we have to use curly braces:
num=2
echo "this is the ${num}nd"
This prints what you want: this is the 2nd
There are a number of variables that are always automatically set. We will discuss them
further down when we use them the first time.
If you need to handle mathematical expressions then you need to use programs such as
expr (see table below).
Besides the normal shell variables that are only valid within the shell program there are
also environment variables. A variable preceeded by the keyword export is an
environment variable. We will not talk about them here any further since they are
normally only used in login scripts.
Saranya 4
Wipro Technologies
How to Write Shell Script
Expressions are always enclosed in a pair of parentheses ( and ). Note that you should put
blanks (white space) around the parentheses and the operators within them to insure
proper parsing.
• Arithmetic expressions
• Logical expressions
Saranya 5
Wipro Technologies
How to Write Shell Script
These are usually used in if statements for conditional execution. Logical expressions
always evaluate to either the value 1 if true, or value 0 if false. The result of a logical
expression can also be assigned to another variable with the @ operator.
These expressions test whether a variable equals another, or a constant value, or is greater
than or less than another value. There are some special advanced tests, but the basic ones
are indicated by the following operators, with a variable or constant on either side. To get
a variable's value, you must, of course, use the $ operator. Character constants should be
enclosed in a pair of quote characters. Use either single (') or double (") quotes, as long as
you use the same type on both ends of the constant. If the variable being tested may
contain blanks or other special shell characters as part of its value, then it must also be
enclosed in quotes, but only double quotes can be used for variables, e.g., "$a".
( $a < $b )
Tests if the numeric value of variable a is less than the numeric value of variable b. You
will get an error message if either variable does not contain a numeric string.
( $a <= 5 )
Tests if numeric value of variable "a" is less than or equal to numeric value 5. You will
get an error message if the variable a does not contain a numeric string.
( $a > $b )
"Greater than" works the same as "less than". There is also a "greater than or equal" test,
using the operator >= Again, you will get an error message if either variable does not
contain a numeric string.
( $a == 5 )
Tests if the numeric value of variable a is exactly equal to numeric value 5. Again, you
will get an error message if the variable a does not contain a numeric string.
( "$c" == "yes" )
Tests if the string value of variable c is exactly equal to the character string constant yes.
Logical expressions can be modified by the "NOT" (!) operator or combined with the
"OR" (||) or "AND" (&&) operators. Examples:
( ! "$c" == "yes" )
The logical NOT operator -- the exclamation point -- can be put at the front of any
expression (inside the parentheses) to reverse the result of the expression. That is,
whenever the expression would normally give a "true" result, it now gives a "false"
result, and vice-versa. In this example, the overall expression is true whenever the value
of the variable c is not equal to the constant string "yes".
( $a > 1 && $a < 5 )
Tests if the value of variable a is greater than numeric value 1 and also less than numeric
value 5. Both cases have to be true for the expression to evaluate "true".
( $c == "yes" || $c == "YES" )
Tests if value of variable c is either the character string constant yes or the string constant
YES (remember that upper and lower case are not the same). If it is equal to either one,
then the expression evaluates "true".
Saranya 6
Wipro Technologies
How to Write Shell Script
The C-shell has built in tests for existence or characteristics of a file. These tests are
made with a special form of a logical expression in which a variable substitution or
constant string is preceded by a one letter operator introduced with the minus sign. For
example, the expression
( -f "junk" )
evaluates to "true" if there is an existing plain file (not a directory or special file) in the
current directory with the name junk. Similarly, if the first shell script argument was the
string subdir, then the expression
( -d $1 )
evaluates to "true" if subdir is the name of a existing directory (not a plain file) in the
current directory.
The complete list of these operators, and what they test for, is:
A more advanced feature allows you to test if a variable value matches a pattern instead
of just whether it is an exact match to a character string. Read about this in the C-shell
reference manual.
You can execute a command from the shell script and have it read the next few lines in
the script as the command's standard input. You do this by redirecting the standard input
with the special symbol << followed by a "marker" string. The shell reads the lines from
the script following this command until it encounters a line that contains only the marker
string. All the lines it has read (not counting the marker string line) are then fed to the
command as its standard input.
Saranya 7
Wipro Technologies
How to Write Shell Script
Here is an example of a fragment of code from a shell script using this feature:
cat > newfile << EOF
This line from the script will be read by cat.
So will this line.
The next line has the 'EOF' marker string that terminates the cat input.
EOF
You can read a line of input from the standard input of the shell script (usually the
terminal, but could be redirected from a file) and set it as the value of a variable with this
syntax:
set variable=$<
This can be used to "prompt" for information from the user. First use the echo command
to print a question; then read the answer typed by the user at the terminal with the syntax
above.
There are three categories of commands which can be used in shell scripts:
1)UNIX Commands:
Although a shell script can make use of any unix commands here are a number of
commands which are more often used than others. These commands can generally be
described as commands for file and text manipulation.
Command syntax Purpose
echo "some text" write some text on your screen
ls list files
wc -l file count lines in file or
wc -w file count words in file or
wc -c file count number of characters
cp sourcefile destfile copy sourcefile to destfile
mv oldname newname rename or move file
rm file delete a file
search for strings in a file
grep 'pattern' file
Example: grep 'searchstring' file.txt
get data out of fixed width columns of text
Example: get character positions 5 to 9
cut -b colnum file cut -b5-9 file.txt
Do not confuse this command with "cat" which is something
totally different
cat file.txt write file.txt to stdout (your screen)
Saranya 8
Wipro Technologies
How to Write Shell Script
Saranya 9
Wipro Technologies
How to Write Shell Script
Here we use the comma (,) as field separator and print the first
and third ($1 $3) columns. If file.txt has lines like:
Adam Bor, 34, India
Kerry Miller, 22, USA
There is much more you can do with awk but this is a very
common use.
They are not really commands but they are very important concepts.
Pipes : (|) send the output (stdout) of one program to the input (stdin) of another
program.
grep "hello" file.txt | wc -l
Finds the lines with the string hello in file.txt and then counts the lines.
The output of the grep command is used as input for the wc command. You can
concatinate as many commands as you like in that way (within reasonable limits).
Backtick :
The output of a command can be used as command line arguments (not stdin as
above, command line arguments are any strings that you specify behind the command
such as file names and options) for another command. You can as well use it to assign
the output of a command to a variable.
The command
find . -mtime -1 -type f -print
finds all files that have been modified within the last 24 hours (-mtime -2 would be 48
hours). If you want to pack all these files into a tar archive (file.tar) the syntax for tar
would be:
tar xvf file.tar infile1 infile2 ...
Instead of typing it all in you can combine the two commands (find and tar) using
backticks. Tar will then pack all the files that find has printed:
#!/bin/sh
# The ticks are backticks (`) not normal quotes ('):
tar -zcvf lastmod.tar.gz `find . -mtime -1 -type f -print`
Saranya 10
Wipro Technologies
How to Write Shell Script
3) Control structures :
The "if" statement tests if the condition is true (exit status is 0, success). If it is the
"then" part gets executed:
if ....; then
....
elif ....; then
....
else
....
fi
Most of the time a very special command called test is used inside if-statements. It can be
used to compare strings or test if a file exists, is readable etc...
The "test" command is written as square brackets " [ ] ". Note that space is significant
here: Make sure that you always have space around the brackets. Examples:
[ -f "somefile" ] : Test if somefile is a file.
[ -x "/bin/ls" ] : Test if /bin/ls exists and is executable.
[ -n "$var" ] : Test if the variable $var contains something
[ "$a" = "$b" ] : Test if the variables "$a" and "$b" are equal
Run the command "man test" and you get a long list of all kinds of test operators for
comparisons and files.
Using this in a shell script is straight forward:
#!/bin/sh
if [ "$SHELL" = "/bin/bash" ]; then
echo "your login shell is the bash (bourne again shell)"
else
echo "your login shell is not bash but $SHELL"
fi
The variable $SHELL contains the name of the login shell and this is what we are testing
here by comparing it against the string "/bin/bash"
4)Shortcut operators :
The case statement can be used to match (using shell wildcards such as * and ?) a given
string against a number of possibilities.
case ... in
...) do something here;;
esac
Let's look at an example. The command file can test what kind of file type a given file is:
file lf.gz
returns:
Here you notice that we use a new special variable called $1. This variable contains the
first argument given to a program. Say we run
smartzip articles.zip
then $1 will contain the string articles.zip
The select statement is a bash specific extension and is very good for interactive use. The
user can select a choice from a list of different values:
select var in ... ; do
break
done
.... now $var can be used ....
Here is an example:
#!/bin/sh
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
break
done
echo "You have selected $var"
Here is what the script does:
What is your favourite OS?
Saranya 12
Wipro Technologies
How to Write Shell Script
1) Linux
2) Gnu Hurd
3) Free BSD
4) Other
#? 1
You have selected Linux
In the shell you have the following loop statements available:
while ...; do
....
done
The while-loop will run while the expression that we test for is true. The keyword
"break" can be used to leave the loop at any point in time. With the keyword "continue"
the loop continues with the next iteration and skips the rest of the loop body.
The for-loop takes a list of strings (strings separated by space) and assigns them to a
variable:
for var in ....; do
....
done
The following will e.g. print the letters A to C on the screen:
#!/bin/sh
for var in A B C ; do
echo "var is $var"
done
A more useful example script, called showrpm, prints a summary of the content of a
number of RPM-packages:
#!/bin/sh
# list a content summary of a number of RPM packages
# USAGE: showrpm rpmfile1 rpmfile2 ...
# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
for rpmpackage in $*; do
if [ -r "$rpmpackage" ];then
echo "=============== $rpmpackage =============="
rpm -qi -p $rpmpackage
else
echo "ERROR: cannot read file $rpmpackage"
fi
done
Above you can see the next special variable, $* which contains all the command line
arguments. If you run
showrpm openssh.rpm w3m.rpm webgrep.rpm
then $* contains the 3 strings openssh.rpm, w3m.rpm and webgrep.rpm.
The GNU bash knows until-loops as well but generally while and for loops are sufficient.
5)Flow-of-control statements
if and foreach are the basic flow-of-control statements. There are more advanced ones
named switch and while which are similar to the statements of the same name in the C
language.
Saranya 13
Wipro Technologies
How to Write Shell Script
if
This form will execute command (which can have a long list of arguments) if the
logical__expression is true. This expression can be one of the logical or file testing
expressions described above. For example, you can test if a file whose name is stored in
the shell's built-in variable $1 (first argument to the shell script) exists as a plain file, and
if so, make a backup copy of it with:
if ( -f $1 ) cp $1 $1.bak
This simple cp command will not work if given a directory to copy, which is why there
is the test for a "plain" file.
• if ( logical_expression ) then
This form allows you to execute more than one command if the expression is true. The
then keyword must follow the logical_expression test on the same line, and the endif
keyword must be on a line by itself to end the entire if command.
The else statement is optional. If you use this, the else keyword must be on a line by
itself. The following lines up to the endif are executed if the expression was false.
The "blocks of commands" may in turn contain additional nested if commands. Just be
sure that each if has a matching endif statement enclosed in the same block.
• foreach
This statement allows you to execute a loop, like a do statement in Fortran or a for
statement in C.
The syntax is:
Saranya 14
Wipro Technologies
How to Write Shell Script
Before passing any arguments to a program the shell tries to expand wildcards and
variables. To expand means that the wildcard (e.g. *) is replaced by the appropriate file
names or that a variable is replaced by its value. To change this behaviour you can use
quotes: Let's say we have a number of files in the current directory. Two of them are jpg-
files, mail.jpg and tux.jpg.
#!/bin/sh
echo *.jpg
This will print "mail.jpg tux.jpg".
Quotes (single and double) will prevent this wildcard expansion:
Saranya 15
Wipro Technologies
How to Write Shell Script
#!/bin/sh
echo "*.jpg"
echo '*.jpg'
This will print "*.jpg" twice.
Single quotes are most strict. They prevent even variable expansion. Double quotes
prevent wildcard expansion but allow variable expansion:
#!/bin/sh
echo $SHELL
echo "$SHELL"
echo '$SHELL'
This will print:
/bin/bash
/bin/bash
$SHELL
Finally there is the possibility to take the special meaning of any single character away by
preceeding it with a backslash:
echo \*.jpg
echo \$SHELL
This will print:
*.jpg
$SHELL
Here documents
Here documents are a nice way to send several lines of text to a command. It is quite
useful to write a help text in a script without having to put echo in front of each line. A
"Here document" starts with << followed by some string that must also appear at the end
of the here document. Here is an example script, called ren, that renames multiple files
and uses a here document for its help text:
#!/bin/sh
# we have less than 3 arguments. Print the help text:
if [ $# -lt 3 ] ; then
cat <<HELP
ren -- renames a number of files using sed regular expressions
HELP
exit 0
fi
OLD="$1"
NEW="$2"
# The shift command removes one argument from the list of
# command line arguments.
shift
shift
# $* contains now all the files:
for file in $*; do
if [ -f "$file" ] ; then
newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"`
if [ -f "$newfile" ]; then
echo "ERROR: $newfile exists already"
else
Saranya 16
Wipro Technologies
How to Write Shell Script
Functions
As soon as you have a more complex program you will find that you use the same code in
several places and also find it helpful to give it some structure. A function looks like this:
functionname()
{
# inside the body $1 is the first argument given to the function
# $2 the second ...
body
}
You need to "declare" functions at the beginning of the script before you use them.
Here is a script called xtitlebar which you can use to change the name of a terminal
window. If you have several of them open it is easier to find them. The script sends an
escape sequence which is interpreted by the terminal and causes it to change the name in
the titlebar. The script uses a function called help. As you can see the function is defined
once and then used twice:
#!/bin/sh
# vim: set sw=4 ts=4 et:
help()
{
cat <<HELP
xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole
HELP
exit 0
Saranya 17
Wipro Technologies
How to Write Shell Script
We have seen that $* and $1, $2 ... $9 contain the arguments that the user specified on
the command line (The strings written behind the program name). So far we had only
very few or rather simple command line syntax (a couple of mandatory arguments and
the option -h for help). But soon you will discover that you need some kind of parser for
more complex programs where you define your own options. The convention is that all
optional parameters are preceeded by a minus sign and must come before any other
arguments (such as e.g file names).
There are many possibilities to implement a parser. The following while loop combined
with a case statement is a very good solution for a generic parser:
#!/bin/sh
help()
{
cat <<HELP
This is a generic command line parser demo.
USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2
HELP
exit 0
}
while [ -n "$1" ]; do
case $1 in
-h) help;shift 1;; # function help is called
-f) opt_f=1;shift 1;; # variable opt_f is set
-l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2
--) shift;break;; # end of options
-*) echo "error: no such option $1. -h for help";exit 1;;
*) break;;
esac
done
Saranya 18
Wipro Technologies
How to Write Shell Script
opt_l is hello
first arg is -somefile1
2nd arg is somefile2
How does it work? Basically it loops through all arguments and matches them against the
case statement. If it finds a matching one it sets a variable and shifts the command line by
one. The unix convention is that options (things starting with a minus) must come first.
You may indicate that this is the end of option by writing two minus signs (--). You need
it e.g with grep to search for a string starting with a minus sign:
Search for -xx- in file f.txt:
grep -- -xx- f.txt
Our option parser can handle the -- too as you can see in the listing above.
Command substitution
The result of any command, meaning whatever it would write to standard output, can be
"captured" by the shell and used to set the value of a variable or as part or all of the
arguments to another command. This is different from sending the output through a pipe
to be the input of another command. Here, the output of one command becomes the
arguments of another command, not the input file. This is called "command
substitution".
Capturing the output of a command is requested by enclosing that entire command with
its arguments in a set of matching backwards quote marks (`) (also called "accent" mark
or "grave" mark). This is not the same character as the apostrophe or single quote, which
slants forward (').
In captured output, all newline characters (end-of-line markers) are changed to blanks, so
the lines in the captured output are joined into one long string of words. Empirical tests
on pangea show that the total length of this captured string can be at least 50,000 bytes.
Other Unix systems may have smaller limits. All should allow at least 4,000 bytes in
command substitution captured strings.
The shell stores captured output in a temporary area of memory. Once you have captured
the output of a command, of course you want to do something with it. You can assign the
output to a variable, or use it as part or all of the arguments to another command.
Examples:
• If a file contains a list of names, you can assign the contents of that file to a
variable by capturing the output of a cat command that is listing the file to standard
output . Here is the kind of line you might use for that in a shell script:
Saranya 19
Wipro Technologies
How to Write Shell Script
• You could tell the mv command to move all the files whose names are listed in the
file list to the new directory newdir with this command:
Script Examples
Now we have discussed almost all components that you need to write a script. All good
scripts should have help and you can as well have our generic option parser even if the
script has just one option. Therefore it is a good idea to have a dummy script, called
framework.sh, which you can use as a framework for other scripts. If you want to write a
new script you just make a copy:
cp framework.sh myscript
and then insert the actual functionality into "myscript".
The script b2d converts a binary number (e.g 1101) into its decimal equivalent. It is an
example that shows that you can do simple mathematics with expr:
#!/bin/sh
# vim: set sw=4 ts=4 et:
help()
{
cat <<HELP
error()
{
# print an error and exit
echo "$1"
Saranya 20
Wipro Technologies
How to Write Shell Script
exit 1
}
lastchar()
{
# return the last character of a string in $rval
if [ -z "$1" ]; then
# empty string
rval=""
return
fi
# wc puts some space behind the output this is why we need sed:
numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
# now cut out the last char
rval=`echo -n "$1" | cut -b $numofchar`
}
chop()
{
# remove the last character in string and return it in $rval
if [ -z "$1" ]; then
# empty string
rval=""
return
fi
# wc puts some space behind the output this is why we need sed:
numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
if [ "$numofchar" = "1" ]; then
# only one char in string
rval=""
return
fi
numofcharminus1=`expr $numofchar "-" 1`
# now cut all but the last char:
rval=`echo -n "$1" | cut -b 0-${numofcharminus1}`
}
while [ -n "$1" ]; do
case $1 in
-h) help;shift 1;; # function help is called
--) shift;break;; # end of options
-*) error "error: no such option $1. -h for help";;
*) break;;
esac
done
while [ -n "$binnum" ]; do
lastchar "$binnum"
Saranya 21
Wipro Technologies
How to Write Shell Script
The algorithm used in this script takes the decimal weight (1,2,4,8,16,..) of each digit
starting from the right most digit and adds it to the sum if the digit is a 1. Thus "10" is:
0*1+1*2=2
To get the digits from the string we use the function lastchar. This uses wc -c to count the
number of characters in the string and then cut to cut out the last character. The chop
function has the same logic but removes the last character, that is it cuts out everything
from the beginning to the character before the last one.
Perhaps you are one of those who save all outgoing mail to a file. After a couple of
months this file becomes rather big and it makes the access slow if you load it into your
mail program. The following script rotatefile can help you. It renames the mail folder,
let's call it out mail, to outmail.1 if there was already an outmail.1 then it becomes
outmail.2 etc...
#!/bin/sh
# vim: set sw=4 ts=4 et:
ver="0.1"
help()
{
cat <<HELP
rotatefile -- rotate the file name
USAGE: rotatefile [-h] filename
version $ver
HELP
exit 0
}
error()
{
echo "$1"
exit 1
Saranya 22
Wipro Technologies
How to Write Shell Script
}
while [ -n "$1" ]; do
case $1 in
-h) help;shift 1;;
--) break;;
-*) echo "error: no such option $1. -h for help";exit 1;;
*) break;;
esac
done
# input check:
if [ -z "$1" ] ; then
error "ERROR: you must specify a file, use -h for help"
fi
filen="$1"
# rename any .1 , .2 etc file:
for n in 9 8 7 6 5 4 3 2 1; do
if [ -f "$filen.$n" ]; then
p=`expr $n + 1`
echo "mv $filen.$n $filen.$p"
mv $filen.$n $filen.$p
fi
done
# rename the original file:
if [ -f "$filen" ]; then
echo "mv $filen $filen.1"
mv $filen $filen.1
fi
echo touch $filen
touch $filen
How does the program work? After checking that the user provided a filename we go into
a for loop counting from 9 to 1. File 9 is now renamed to 10, file 8 to 9 and so on. After
the loop we rename the original file to 1 and create an empty file with the name of the
original file.
There are two methods you can use to execute a shell script.
First, you can give the script file name as an argument to an instance of the shell
program, that is, type a command like:
csh filename
csh is the name of the C-shell program itself. This command starts up a new C-shell
process that executes the commands in the script filename and then terminates.
Saranya 23
Wipro Technologies
How to Write Shell Script
Second, you can give the name of the shell script itself as a command, just like any other
program on Unix. First, you have to let the Unix kernel recognize that this is a shell script
by doing the following two steps. Then you can simply type the shell's filename as a
command name to execute it. The kernel will automatically start up a new C-shell
process to execute the commands in the script.
Note that your login shell has to be able to find the shell script file when you type its
name as a command. The login shell only looks in a set of specific directories, called its
path, to find files that contain programs. On pangea, the default path includes your
current working directory, so you can run a shell script in the current directory simply by
typing its name. Otherwise, type the absolute pathname of the script (for example,
/home/sysop/farrell/programs/addup), or add the directory where the script lives to your
standard path. Whenever you add a directory to your standard path, you must run the
rehash built-in C-shell command to tell your login shell to rebuild its list of programs
using your new path definition.
To make your shell script file executable as a program, do these steps:
Put the following line as the first line in your script:
#! /bin/csh -f
This is a special comment telling the kernel that you want this script to be executed by
the C-shell (there is an alternate shell named simply sh). The -f option helps the
command to start up faster by skipping the initial read of the .cshrc file.
Use chmod to set execute permission for your file. For instance, if you want anyone to be able to execute the script
file, use
chmod ugo+x filename
Debugging
The most simple debugging help is of course the command echo. You can use it to print
specific variables around the place where you suspect the mistake. This is probably what
most shell programmers use 80% of the time to track down a mistake. The advantage of a
shell script is that it does not require any re-compilation and inserting an "echo"
statement is done very quickly.
The shell has a real debug mode as well. If there is a mistake in your script
"strangescript" then you can debug it like this:
sh -x strangescript
This will execute the script and show all the statements that get executed with the
variables and wildcards already expanded.
The shell also has a mode to check for syntax errors without actually executing the
program. To use this run:
sh -n your_script
Saranya 24
Wipro Technologies
How to Write Shell Script
• Changing Directories
Always check the error status of chdir, to avoid running commands in the wrong
directory. Alternatively, use fully qualified paths to obviate the need the chdir.
#!/bin/sh
cd $nosuchdir || exit 1
rsync elsewhere:/foo .
Without || exit 1 to abort the script should cd fail, the subsequent rsync command
could move files to the wrong location or fill up the wrong partition.
• Redirection
Redirection can take place (almost) anywhere, not just at the end.
$ echo a b >c
$ echo >c a
b
$ >c echo a b
Placing the filename at the beginning allows easier editing of the search term at
the end of the command.
user1
Saranya 25
Wipro Technologies
How to Write Shell Script
ci
Depending on the editor, one can use concept above to open certain files for
editing, for example, files in a cvs sandbox that have conflicts.
vi
ex/vi: Vi's standard input and output must be a terminal
bbedit
The bbedit utility is part of BBEdit for Mac OS X, and avoids terminal issues by
sending the files to the BBEdit application. Using emacs in server/client mode
may avoid this problem for emacs. The alternative is to use backticks to make the
files available as arguments to the program, instead of feeding them in through
xargs.
xargs can be chained with other programs. For instance, one may want to find perl
scripts containing the text While and do something with them.
Saranya 26
Wipro Technologies
How to Write Shell Script
\
| xargs perl -i -ple 's/While/while/g'
xargs will fail or do the wrong thing if passed filenames contain spaces. This is
common on filesystems that traditionally have allowed spaces in filenames (Mac
OS), or where file trees have been uploaded to Unix from other platforms. If using
find/xargs pairs, the spaces-in-filenames problems can be avoided as follows.
• Stubborn Files
Dealing with files that have odd characters in their names can often be a chore on
Unix, as one cannot type in the names in question. One could use a graphical file
manager tool, but I find those cumbersome, ill suited to dealing with large
numbers of files, and usually not installed on server systems.
Files that being with a hyphen (such as a file -rf) will trigger option
processing, as the shell is very stupid. These can be avoided by either
disabling option processing, or prefixing a directory name to the file path.
$ ls
-rf
$ rm -rf
$
ls
-rf
$ rm *
$ ls
-rf
$ rm
Saranya 27
Wipro Technologies
How to Write Shell Script
-- -rf
$ ls
$ touch ./-rf
$
ls
-rf
$ rm ./-rf
$ ls
$
Each file on a Unix filesystem has a inode number associated with it;
knowing the inode number of the bad file allows us to search for and
delete it.
$ ls -i *
615383 foo
$ find . -inum
615383 -exec rm {} \;
Saranya 28
Wipro Technologies
How to Write Shell Script
For situations where the mangled filenames are in deep directory trees, or
where the mangling is consistent (uploaded filenames from a DNA
sequencer come to mind), use File::Find and write a standalone script.
$ rm -rf /the/bad/dir
$ perl -le 'opendir
/the/bad/dir
ls
Commands can be run over ssh(1), though how the shell handles more complex
commands can cause problems.
hostname
server.example.org
client$ ssh example.org sleep 3
&& hostname
client.example.org
Saranya 29
Wipro Technologies
How to Write Shell Script
The && is handled by the local shell, not the remote server. Quoting can fix the
problem.
hostname'
server.example.org
• Clobbering a File
There are several ways one can empty the contents of an existing file without
removing and touch(1)-ing the file in question. Using echo(1) is not portable, as
some systems do not support the -n flag, such as Digital UNIX without
CMD_ENV=bsd set. The use of the shell null operator : is a clever way, and saves
typing.
>file
$ : >file
grep can match itself. To avoid this problem, the regular expression can be altered
so grep does not match itself, which is easier than appending | grep -v grep to a
command.
The regular expression ss[h] cannot match the literal string ssh[h] in the process
listing, but will match any process name containing ssh. Another option: use
commands such as pgrep(1).
Some utilities are controlled via command interfaces. Full control of command
interfaces may require the expect utility, or Expect. Simple needs can be met by
Saranya 30
Wipro Technologies
How to Write Shell Script
printing commands on standard input, then parsing the output. For example, the
Mac OS X scutil can be queried for information:
'/PrimaryService/{print $3}'
open
get
State:/Network/Global/IPv4
d.show
EOF`
echo $SERVICE_GUID
If parsing a list of filenames from a file, spaces in filenames may cause shell
Interpolation problem.
To workaround, use xargs -0, and convert the file to a null delimited list:
test
$ < test tr '\n' '\0' | xargs -0 file
foo bar:
empty
• Argument Processing
Saranya 31
Wipro Technologies
How to Write Shell Script
Reference:
1. http://user.it.uu.se/~matkin/documents/shell/
2. http://www.injunea.demon.co.uk/pages/page212.htm
3. http://www.washington.edu/computing/unix/shell.html
4. http://www.freeos.com/guides/lsst/ch02sec01.html
5. http://www.fmrib.ox.ac.uk/fslcourse/lectures/scripting/s_0060.htm
Saranya 32
Wipro Technologies