MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


An Introduction to Tiger Terminal, Part 4

by Mary Norbury-Glaser
09/09/2005

In parts one, two, and three of this series, we covered basic commands that can be executed through the OS X Tiger Terminal app. Today, I'll take a bit of a leap and introduce shell scripting.

What are shell scripts, and why would you want to create them? Well, if you begin to use the Terminal with regularity, you'll find that you are repeating many of the same commands, often in sequence, in order to accomplish some task. You can automate these tasks by writing a shell script file.

What You Need To Know--And Do

In part one of this series, we ran the cat /etc/shells command to see a list of shells available in Mac OS X Tiger. Each shell has a standard scripting language associated with it. Since our default shell in Mac OS X Tiger is bash, we'll stick with that for this scripting tutorial.

You'll also need a text editor to write your scripts. In part one, I introduced Mac OS X Tiger's new default text editor, nano, which we'll use here to write and save our new shell scripts. You can use any text editor, whether it's a command-line editor or a graphical editor like SubEthaEdit (www.codingmonkeys.de/subethaedit) or BBEdit (www.barebones.com/products/bbedit/index.shtml). If you choose a graphical editor, make sure you select the option to use Unix line breaks when saving files.

Let's assume you'll be creating shell scripts on a regular basis, so it's the best practice to create a directory where you'll save all your scripts. Create a new folder in your /Documents directory and name it scripts. From the Terminal command line, now!

norburym15:~ norburym$ cd Documents
norburym15:~/Documents norburym$ mkdir scripts
norburym15:~/Documents norburym$

Remember that your default location when you open the Terminal app is your home directory. Here, you've changed into your Documents directory and then created a new directory (folder) called scripts, using the mkdir (make directory) command. The Terminal doesn't tell you it's been created nor does it move you into the directory automatically. Run the ls command at the last prompt above to see that the new directory has, indeed, been created:

figure
Figure 1. ~/Documents/scripts folder

The next thing you'll want to do is tell the shell where to find this directory. You'll do that by adding the location of your new scripts directory to the PATH variable in your .bash_profile file. Huh? OK, good time for an aside! When you open the Terminal app, the default shell, bash, starts up and reads some files in order to get configuration information. The primary files are bashrc and profile, which live in the system /etc directory (partial listing shown below):

figure
Figure 2. bash files

However, to set user-specific configuration variables, it's best to create a file in your own home directory that bash will read when it starts up. bash will read the system-wide files first, and then the user-specific ones. The user-specific file settings will override those in the system-wide files.

You can see your current PATH by executing the echo $PATH command at the shell prompt:

norburym15:~ norburym$ echo $PATH
/bin:/sbin:/usr/bin:/usr/sbin
norburym15:~ norburym$
Mac OS X Tiger for Unix Geeks

Related Reading

Mac OS X Tiger for Unix Geeks
By Brian Jepson, Ernest E. Rothman

In part one, we created a file called .bash_profile in our home directory that contained a set of aliases for the cp, rm, and mv commands. This file is actually a shell script file: it contains three commands that run in sequence when the .bash_profile file is read. This file turns out to be exactly where we want to set our new PATH environment variable! Open a new Terminal window and type in ls -a to show a listing of all files, including the hidden files indicated by the period in front of their names, and you'll see your .bash_profile file:

figure
Figure 3. .bash_profile

You'll open .bash_profile using nano and add the path to the scripts folder in the system environment using the export command (if you didn't follow along in part one and create this file, you'll do so now):

figure
Figure 4. Add PATH variable

Remember that to write to this file and save it, you will need to use the keystrokes ^O (WriteOut) and then ^X (Exit). Back at the Terminal, activate the changes by typing:

norburym15:~ norburym$ source .bash_profile

If you run the echo $PATH command again, you'll see this:

figure
Figure 5. New PATH details

Now, we're ready! Let's get started!

First Shell Script

Let's create a shell script to see who the users are on my machine:

1. Open a new Terminal window.
2. cd to your new scripts directory:

figure
Figure 6. cd to scripts

3. Create a new shell script by calling nano and naming your new script firstscript.sh.

figure
Figure 7. nano firstscript.sh

4. When you hit the Return key, nano will open a new file called firstscript.sh.

figure
Figure 8. firstscript.sh

5. Type in the following:

figure
Figure 9. Contents of firstscript.sh

6. Then save the file (^O) and exit (^X). Take a look in your scripts folder and you'll see your newly created first shell script!

figure
Figure 10. First shell script

OK, but what does all that content in the script mean? A nice habit to get into when scripting is to add comments describing what you're trying to accomplish and what some of the command lines are aiming to do. You can comment out lines using the # symbol. The shell ignores these lines. As you create more and more scripts, you should include a header that describes the function of the script, your name as author, and the creation and modification dates. Here's what the script would look like with comments:

figure
Figure 11. Adding comments

The #!/bin/bash line at the beginning tells the bash shell program to translate this script, even if your default shell is a different one. Although this line begins with a #, it's not commented out, and it's not ignored by the shell. The #! (called "shebang" for "hash bang" or "sharp bang") is magic: when the OS runs the file, it reads the first few bytes of the file (the "magic number") to find out what kind of file it is. The #! byte pattern means it's a shell script, and the rest of the line (in this case, /bin/bash) indicates which binary should run the script file. For example, if you're using the tcsh shell, the shebang line would be #!/bin/tcsh; if you're using perl, it would be #!/bin/perl.

Let's try running our script:

figure
Figure 12. run firstscript.sh

Uh-oh. "Permission denied." If we run ls -l against the file to check permissions, we see this:

figure
Figure 13. Permissions check

Ten points to Gryffindor if you see the solution! In part two, I illustrated the information we get by running the ls -l command. The series of letters (-rw-r--r--) before the file give us the file type (the first character) and the file mode code corresponding to permissions for owner, group, and everyone else (the following nine characters):

  1. The first character (-) is the file type; in this case, the dash means "plain file;" it could also be d for directory, or l for symbolic link.
  2. The next three characters (rw-) are the owner's file permissions: norburym can read (r) and write (w) but not execute (-).
  3. The next three characters (r--) are the group file permissions: group norburym can read (r) but can't write (-) and can't execute.
  4. The final three characters (r--) are file permissions for "everyone else:" they can read (r) but can't write (-) and can't execute (-).

We need to make the file executable! We do that by using the chmod (change mode) command with the +x option (adding permission to execute) and then we check the permissions again:

figure
Figure 14. chmod +x firstscript.sh

We see that the permissions have changed to -rwxr-xr-x, which means that we have added the x, or executable, switch to the user, group, and other permissions. Once you've made this permissions change with chmod, your text editor will preserve this permission, even if you make subsequent changes to your script.

Now we're ready to try again:

figure
Figure 15. Success!

The shell script runs successfully. Let's take a closer look at what is actually going on: when the bash shell reads the first line of our shell script, #!/bin/bash, it actually generates a new bash shell that reads and executes the commands from our firstscript.sh script. At the completion of command execution, the new shell terminates automatically and we're left at our original shell.

We took some pains to create our PATH statement in our .bash_profile file. If we hadn't done that, and we had tried to execute the shell script from our default terminal window (which opens with our location set to our home directory, not our /Documents/scripts directory), then we would have gotten the error "command not found." We would have needed to tell bash where our script is located with:

norburym15:~ norburym$ documents/scripts/firstscript.sh

With our PATH statement telling bash where to look for our default scripts directory, we can type our command from any directory and bash knows where to look for our script: in the default scripts folder in our /Documents folder.

Less is More

The output of our script scrolled out pretty quickly. Sometimes, it's desirable to see the output of our script in smaller chunks. We can use the "pipe" symbol (|) and the less command to see the output in more manageable chunks. The pipe symbol is done with the Shift and \ (backslash) keys. The command looks like this:

norburym15:~ norburym$ firstscript.sh | less

And the output looks like this:

figure
Figure 16. Output of firstscript.sh | less

Use the Return key to move to the next line of output. Use the Q key to quit out of less and bring you back to your shell prompt.

Another Simple Script

Now that I have successfully written a script that can tell me who has user accounts on my PowerBook, I want to see what these other users have in their Documents folders.

1. Open a new Terminal window.
2. cd to your new scripts directory.
3. Create a new shell script called secondscript.sh with nano:

figure
Figure 17. secondscript.sh without comments

figure
Figure 18. secondscript.sh with comments

4. Save the script (^O) and exit (^X).
5. Make the file executable with chmod +x secondscript.sh.
6.. Run the shell script secondscript.sh and you'll be prompted for your admin password:

figure
Figure 19. secondscript.sh

6. Type in your admin password and hit Return:

figure
Figure 20. After typing in your password

7. Navigate to your documents folder and cat (output) the contents of userfiles.txt.

figure
Figure 21. Output of userfiles.txt

Variables

So far, we've created shell scripts using some of the simple Unix commands that we've learned from previous parts of this tutorial. Every shell program has a scripting language built in, and has special commands that are part of its own scripting language. The standard language for writing Unix scripts is the language of the Bourne shell. Most shells (sh, bash, ksh, and zsh) will run it. However, csh and tcsh run a different scripting language from these others. When OS X 10.3 (Panther) was released, with its new default bash shell, it caused a lot of grumbling among scripters who were comfortable using the default tcsh shell in Mac OS X 10.2 (Jaguar), because many commands have different syntax in bash.

The bash shell has a wide range of programming features, including user-defined functions, conditional statements (if/then), loops (for and while), mathematical operations, and variables. A variable is simply a name associated with a value: a string that represents data. Within bash, there are standard variables defined by the shell itself. Let's use two of them in our next shell script.

1. Open a new Terminal window, navigate to /Documents/scripts, and open nano, naming a new script, thirdscript.sh.

figure
Figure 22. nano thirdscript.sh

2. Create the following script, using your own comments, and tweak the text a bit to show your own flair!

figure
Figure 23. thirdscript.sh

3. Save the script (^O) and exit (^X).

4. Don't forget to change permissions!

norburym15:~/documents/scripts norburym$ chmod +x thirdscript.sh

5. Run it!

figure
Figure 24. The output of thirdscript.sh

Both $USER and $PWD are standard bash variables.

Here is another shell script, using the $HOME variable:

1. Open a new Terminal window, navigate to /Documents/scripts, and open nano naming a new script, fourthscript.sh.

2. Type in the following script:

figure
Figure 25. fourthscript.sh

3. Save the script (^O), exit (^X), and change permissions with chmod +x fourthscript.sh.

4. Run this script:

figure
Figure 26. The output of fourthscript.sh

You can also set your own variables. Here's a classic (and, sorry, woefully overused) example:

1. Still in your scripts folder, open nano and name a new script overused.sh.

figure
Figure 27. nano overused.sh

2. Type the following script, save it (^O), and exit (^X) nano:

figure
Figure 28. overused.sh

3. Permissions magic:

norburym15:~/documents/scripts norburym$ chmod +x overused.sh

4. Run it!

figure
Figure 29. The output of overused.sh

Here, we've created our own variables, MY_GREETING and YOUR_RETORT, and assigned each of them a unique value: the string "Hello World!" and the string "Oh, go jump in the lake!" respectively. The equal sign (=) is called the assignment operator. There are no spaces on either side of the = symbol.

Go Get 'Em, Tiger!

We've obviously only scratched the surface here, but I hope these examples have given you ideas about how to harness the power of the Terminal by creating your own shell scripts! There are some really good books out there to help speed you on your way:

Mary Norbury-Glaser is the IT director at a University of Colorado affiliate center. She has over 15 years of experience in cross-platform systems administration in the education sector. She loves fast cars and geocaching.


Return to the Mac DevCenter

Copyright © 2009 O'Reilly Media, Inc.