Then execute "chmod +x rename" and you have a new UNIX program. If you
want to add some simple
syntax checking to this script, using the techniques I discussed earlier, change the last line to read:
mv ${1?"missing: original filename"} ${2?"missing new filename"}
This isn't very user friendly. If you do not specify the first argument, the script will report:
rename: 1: missing: original filename
As you can see, the missing variable, in this case "1," is reported, which is a little confusing. A second way to
handle this is to assign the positional variables to new names:
#!/bin/sh
# rename: - rename a file
# Usage: rename oldname newname
oldname=$1
newname=$2
mv ${oldname:?"missing"} ${newname:?"missing"}
Click here to get file:
rename.sh
This will report the error as follows:
rename: oldname: missing
Notice that I had to add the colons before the question mark. Earlier I mentioned how the question mark tests
for undefined parameters, while the colon before the question mark complains about empty parameters as
well as undefined parameters. Otherwise, the mv command might have complained that it had insufficient
arguments.
The Bourne shell can have any number of parameters. However, the positional parameters variables are
limited to numbers 1 through 9. You might expect that $10 refers to the tenth argument, but it is the
equivalent of the value of the first argument with a zero appended to the end of the value. The other variable
format, ${10}, ought to work, but doesn't. The Korn shell does support the ${10} syntax, but the Bourne
shell requires work-arounds. One of these is the shift command. When this command is executed, the first
argument is moved off the list, and lost. Therefore one way to handle three arguments follows:
#!/bin/sh
arg1=$1;shift;
arg2=$1;shift;
arg3=$1;shift;
echo first three arguments are $arg1 $arg2 and $arg3
The shift command can shift more than one argument; The above example could be:
#!/bin/sh
arg1=$1
arg2=$2
arg3=$3;shift 3
echo first three arguments are $arg1 $arg2 and $arg3
This technique does make it easier to add arguments, but the error message is unfriendly. All you get is
"cannot shift" as an error. The proper way to handle syntax errors requires a better understanding of testing
Bourne Shell Tutorial
http://www.grymoire.com/Unix/Sh.html
24 of 66
11/21/2011 12:03 PM
and branching, so I will postpone this problem until later.
$0 - Scriptname
There is a special positional parameter, at location zero, that contains the name of the script. It is useful in
error reporting:
echo $0: error
will report "rename: error" when the rename script executes it. This variable is not affected by the shift
command.
$* - All positional parameters
Another work-around for the inability for specifying parameters 10 and above is the "$*" variable. The "*" is
similar to the filename meta-character, in that it matches all of the arguments. Suppose you wanted to write a
script that would move any number of files into a directory. If the first argument is the directory, the following
script would work:
#!/bin/sh
# scriptname: moveto
# usage:
# moveto directory files.....
directory=${1:?"Missing"};shift
mv $* $directory
Click here to get file:
moveto.sh
If this script was called "moveto" then the command
moveto /tmp *
could easily move hundreds of files into the specified directory. However, if any of the files contain a space in
the name, the script would not work. There is a solution, however, using the $@ variable.
$@ - All positional parameters with spaces
The "$@" variable is very similar to the the "$*" variable. Yet, there is a subtle, but important distinction. In
both cases, all of the positional parameters, starting with $1, are listed, separated by spaces. If there are
spaces inside the variables, then "$@" retains the spaces, while "$*" does not. An example will help. Here is a
script, called EchoArgs, that echoes its arguments:
#!/bin/sh
# Scriptname: EchoArgs
# It echoes arguments
#First - make sure we are using the Berkeley style echoes
Bourne Shell Tutorial
http://www.grymoire.com/Unix/Sh.html
25 of 66
11/21/2011 12:03 PM
PATH=/usr/ucb:$path;export PATH
E="echo -n"
# echo the name of the script
${E} $0:
# now echo each argument, but put a space
# before the argument, and place single quotes
# around each argument
${E} " '${1-"?"}'"
${E} " '${2-"?"}'"
${E} " '${3-"?"}'"
${E} " '${4-"?"}'"
${E} " '${5-"?"}'"
${E} " '${6-"?"}'"
${E} " '${7-"?"}'"
echo
Click here to get file:
EchoArgs.sh
Second, here is a script that tests the difference:
#!/bin/sh
EchoArgs $*
EchoArgs $@
EchoArgs "$*"
EchoArgs "$@"
Click here to get file:
TestEchoArgs.sh
Now, let's execute the script with arguments that contain spaces:
./TestEcho "a b c" 'd e' f g
The script outputs the following:
./EchoArgs: 'a' 'b' 'c' 'd' 'e' 'f' 'g'
./EchoArgs: 'a' 'b' 'c' 'd' 'e' 'f' 'g'
./EchoArgs: 'a b c d e f g' '?' '?' '?' '?' '?' '?'
./EchoArgs: 'a b c' 'd e' 'f' 'g' '?' '?' '?'
As you can see, $* and $@ act the same when they are not contained in double quotes. But within double
quotes, the $* variable treats spaces within variables, and spaces between variables the same. The variable
$@ retains the spaces. Most of the time $* is fine. However, if your arguments will ever have spaces in them,
then the $@ is required.
$# - Number of parameters
The "$#" variable is equal to the number of arguments passed to the script. If newscript returned $# as a
results, then both
newscript a b c d
Bourne Shell Tutorial
http://www.grymoire.com/Unix/Sh.html
26 of 66
11/21/2011 12:03 PM