DirectNET

Data Center Management Solutions including UPS Systems, Data Center Cooling, KVM over IP & IP Power Strips, Server Racks and Server Rack accessories; KVM Switches and KVM Extenders; Rackmount Monitors and Rackmount Keyboards.


NAVIGATION
Home
Store
INSIDE MAC
Television Shows
Broadcast Shows
Daily News Shows
Special Shows
EVENTS
DAILY TIPS
Design
Mac OS X
Mac OS X UNIX
COMMUNITY
Forums
Surveys
NEWS
Current
Press
Archive
FEATURES
Editorial
Dr. Mac
Reviews
Reader Reports
RESOURCES
FAQ
Documentation
Learning Center
MAN pages
Glossary
Tutorials
Tips
Links

OUR PARTNERS

OS X | UNIX

back

Unix

Mac OS X Unix Tutorial

Part 8 - Shell Scripting 1 (Page 2 of 2)

What If?

Here is a useful script to solve a common problem when transferring text files between tradition Mac text editors and Unix editors. The script demonstrates the use of the 'if' statement.

Background: Traditionally, a Mac text file uses Return (ASCII 13) as an end of line character. Unix uses Line Feed (ASCII 10) as an end of line character. Edit a Mac-style text file with a Unix editor and you will see the file as one long line interspersed with '^M's.

The following script converts Mac end of line characters into Unix style end of line characters. It has two parts, the first checks that the script was given a file to convert, and the second actually does the conversion.

% cat mac2unix
#!/bin/sh

if [ "$1" = "" ] then echo "Usage: $0 filename" exit fi
tmp=$(./gen-tmp) tr \\r \\n < $1 > $tmp ; mv $tmp $1

The script uses an 'if' statement to decide whether or not to proceed, base on whether or not a filename was given.

if [ "$1" = "" ]

is testing if the first parameter -$1- is empty or not.

If the condition is true (no arguments where given when the script was run) the script executes the 'then' part between 'then' and 'fi'. This simply echoes a usage line and exits the script.

If the condition is false (a filename was given), the 'if' statement is skipped - i.e. everything up to 'fi' is skipped. The script proceeds with the line 'tmp=$(./gen-tmp)'.

The line:

tmp=$(./gen-tmp)

is calling script 'gen-tmp' and setting the variable 'tmp' to equal its output (just as we did for 'date=$(date)' in the 'vars.sh' script above). 'gen-tmp' generates a temporary filename (below).

The line:

tr \\r \\n < $1 > $tmp ; mv $tmp $1

is doing the actual conversion using the Unix command 'tr'. Note the use of $tmp and $1. Part 7 explains the redirection operators '<' and '>', and 'man tr' will explain how 'tr' itself works.

The second script looks like this:

% cat gen-tmp 
#!/bin/sh
tmp=/tmp/$UID$(date +%H%M%S)
echo $tmp

The script sets variable tmp to be '/tmp/' followed by the user's UID and the current time, in order to generate a unique filename - or rather pathname. The line 'echo $tmp' causes the output of the script to be the generated filename.

For example:

% ./gen-tmp 
/tmp/501130202

The filename is grabbed by the first script and assigned to variable 'tmp' in the line:

tmp=$(./gen-tmp)

If .. Else

The mac2unix script can be improved by removing the 'exit' statement from within 'if...fi' and using and 'if...else...fi' construct. What we really want to say is this:

if no filename given
  output a usage line
else
  do the conversion
fi

The 'else' part is executed when the 'if' condition is false, so either the 'if' part or the 'else' part is executed, but never both.

Here is the new script:

% cat mac2unix2
#!/bin/sh

if [ "$1" = "" ] then echo "Usage: $0 filename" else tmp=$(./gen-tmp) tr \\r \\n < $1 > $tmp ; mv $tmp $1 fi
 
Tell Me More...

Parameters to 'echo'

What is the difference between:

echo Adrian    Mayo

and

echo "Adrian   Mayo"

The first passes two parameters/arguments to echo, 'Adrian' and 'Mayo', which are echoed with a single space between them.

The second passes a single argument to echo, which is echoed verbatim, with many spaces.

Globbing

When issuing a command such as:

ls -l *

'ls' does not get the star. Instead, the shell interprets the star as a wildcard and expands it to be all the files in the current directory.

'ls' sees the first argument '-l', then one argument for each file in the current directory.

Try the same with the 'params.sh' script:

./params.sh *

Why the Double Quotes

in "$1" = ""?

Without them, the statement would read:

[ $1 = ]

which is illegal syntax.

Even a condition like:

[ $1 = Hello ]

requires quotes. If $1 were empty the condition would expand to:

[ = Hello ]

which again is illegal syntax.

UID?

$UID is an environment variable that is always available and set to the user's UID (see Part 4.)

Try:

echo $UID

on the command line (Bourne shell).

Indentation

Notice that I have indented some lines in both of the scripts. This makes the script easier to read. The control constructs stand out from the statements within them, so it is easier to pick out the alternative paths through the script.


The tcsh

Here are the 'tcsh' versions of the scripts.

mac2unix:

% cat mac2unix.tcsh
#!/bin/tcsh

if ("$1" == "") then echo "Usage: $0 filename" exit endif
set tmp=`./gen-tmp.tcsh` tr \\r \\n < $1 > $tmp ; mv $tmp $1

gen-tmp:

% cat gen-tmp.tcsh 
#!/bin/tcsh
set tmp=/tmp/$uid`date +%H%M%S`
echo $tmp

mac2unix2:

% cat mac2unix2.tcsh
#!/bin/tcsh

if ("$1" == "") then echo "Usage: $0 filename" else set tmp=`./gen-tmp.tcsh` tr \\r \\n < $1 > $tmp ; mv $tmp $1 endif

Notice the slightly different 'if' syntax and the double equals:

if [ "$1" = "" ] then

versus

if ("$1" == "") then

and the fact that the 'then' part is on the same line for 'tcsh'.

Other differences are as already noted in the previous examples.


Getting Input From the User

Sometimes you may require input from the user while the script is running. The Bourne shell provides the 'read' command to write a prompt to the Terminal and wait for the user to type some text. This text is then assigned to a variable and can be used throughout the rest of the script.

For example:

% cat input 
#!/bin/sh

read -p "give your name> " name echo Hello $name

Running the script gives the following:

% ./input
give your name>

at this point the script is waiting for me to type something then press return. Continuing:

give your name> Adrian Mayo
Hello Adrian Mayo

It would be easy to change 'mac2unix' to ask for a filename in when none is given, instead of printing the usage message and finishing.

 
Tell Me More...

TIP

When calling a Unix command line or another script, it is advisable to use the full pathname, for example:

/usr/bin/tr

instead of

tr

This avoids reliance on the $PATH environment variable or the current directory. My use of './gen-tmp' is not recommended as 'mac2unix' will not run unless 'gen-tmp' is in the current directory.

Alternatively, you might set up the PATH variable explicitly within your shell script to avoid problems where different Unix systems have commands in different places.


Next Part

In Parts 9 and 10, I will continue with shell scripting, introducing case statement, loops, functions, complex conditions, and other goodies.

Until then, consider:

if [ "$1" = "" ]; then

can be used instead of :

if [ "$1" = "" ]
then

Can you think why this should work?

Enjoy :-)



Discuss this article in the Learning Center forum




previous

Part 8 - Shell Scripting 1 (Page 2 of 2)

end

Copyright © 2000-2009 Inside Mac Media, Inc. All rights reserved.
Apple assumes no responsibility with regard to the selection, performance, or use of the products or services. All understandings, agreements, or warranties, if any, take place directly between the vendors and prospective users.
Apple, the Apple logo, Mac, PowerMac G4, PowerMac G5, Xserve, Xserve RAID, PowerBook, iBook, Airport, AirPort Extreme, iMac, eMac, iLife, iMovie, iCal, iPhoto, iTunes, QuickTime, FireWire, iPod, iSight, AppleWorks, Macintosh, Jaguar, Panther, Mac OS, Mac OS X and Mac OS X Server are trademarks of Apple Computer, Inc.