CHAPTER 4 Basic Shell Programming
If you have become familiar with the customization techniques we presented in the previous chapter, you have probably run into various modifications to your environment that you want to make but cantyet. Shell programming makes these possible.
bash has some of the most advanced programming capabilities of any command interpreter of its type. Although its syntax is nowhere near as elegant or consistent as that of most conventional programming languages, its power and flexibility are comparable. In fact, bash can be used as a complete environment for writing software prototypes.
Some aspects of bash programming are really extensions of the customization techniques we have already seen, while others resemble traditional programming language features. We have structured this chapter so that if you arent a programmer, you can read this chapter and do quite a bit more than you could with the information in the previous chapter. Experience with a conventional programming language like Pascal or C is helpful (though not strictly necessary) for subsequent chapters. Throughout the rest of the book, we will encounter occasional programming problems, called tasks, whose solutions make use of the concepts we cover.
Shell Scripts and Functions
A script (a file that contains shell commands) is a shell program. Your .bash_profile and environment files, discussed in the previous chapter, are shell scripts.
You can create a script using the text editor of your choice. Once you have created one, there are two ways to run it. One, which we have already covered, is to type source scriptname. This causes the commands in the script to be read and run as if you typed them in.
The second way to run a script is simply to type its name and hit RETURN, just as if you were invoking a built-in command. This, of course, is the more convenient way. This method makes the script look just like any other UNIX command, and in fact several "regular" commands are implemented as shell scripts (i.e., not as programs originally written in C or some other language), including spell, man on some systems, and various commands for system administrators. The resulting lack of distinction between "user command files" and "built-in commands" is one factor in UNIXs extensibility and, hence, its favored status among programmers.
You can run a script by typing its name only if the directory where the script is located is in your command search path, or . (the current directory) is part of your command search path, i.e., the scripts directory path (as discussed in Chapter 3). If these arent in your path, you must type ./scriptname, which is really the same thing as typing the scripts absolute pathname (see Chapter 1).
Before you can invoke the shell script by name, you must also give it "execute" permission. If you are familiar with the UNIX filesystem, you know that files have three types of permissions (read, write, and execute) and that those permissions apply to three categories of user (the files owner, a group of users, and everyone else). Normally, when you create a file with a text editor, the file is set up with read and write permission for you and read-only permission for everyone else.
Therefore you must give your script execute permission explicitly, by using the chmod command. The simplest way to do this is to type:
$ chmod +x scriptname
Your text editor will preserve this permission if you make subsequent changes to your script. If you dont add execute permission to the script and you try to invoke it, the shell will print the message:
scriptname: Permission denied
But there is a more important difference between the two ways of running shell scripts. While using source causes the commands in the script to be run as if they were part of your login session, the "just the name" method causes the shell to do a series of things. First, it runs another copy of the shell as a subprocess; this is called a subshell. The subshell then takes commands from the script, runs them, and terminates, handing control back to the parent shell.
Figure 4-1 shows how the shell executes scripts. Assume you have a simple shell script called alice that contains the commands hatter and gryphon. In .a, typing source alice causes the two commands to run in the same shell, just as if you had typed them in by hand. .b shows what happens when you type just alice: the commands run in the subshell while the parent shell waits for the subshell to finish.
You may find it interesting to compare this with the situation in .c, which shows what happens when you type alice &. As you will recall from Chapter 1, the & makes the command run in the background, which is really just another term for "subprocess." It turns out that the only significant difference between .c and .b is that you have control of your terminal or workstation while the command runs you need not wait until it finishes before you can enter further commands.
There are many ramifications to using subshells. An important one is that the exported environment variables that we saw in the last chapter (e.g., TERM, EDITOR, PWD) are known in subshells, whereas other shell variables (such as any that you define in your .bash_profile without an export statement) are not.
Other issues involving subshells are too complex to go into now; see Chapter 7 and Chapter 8 for more details about subshell I/O and process characteristics, respectively. For now, just bear in mind that a script normally runs in a subshell.