Introduction to Shell Programming


General Introduction

A shell program, sometimes referred to as a shell script1, is simply a program constructed of shell commands. Shell programs are interpreted each time they are run. This means each command is processed (i.e. executed) by the shell a single line at a time. This is different from languages such as C or C++, which are translated in their entirety by a compiler program into a binary image. A shell program may be simple and consist of just a few shell commands, or it may be very complex and consist of thousands2 of shell commands. The complexity of the shell program is in the hand of the programmer. In general, a shell program can be characterized by: As stated, a shell program is merely a file containing shell commands. Thus, if we wanted to write the venerable "hello world" program as a shell script, we could do the following:
  1. use the editor to create the program, for simplicity we'll call hw (recall file extensions are not mandatory)
    	$ vi hw [Enter]
  2. insert the following shell command in the hw file:

    
     echo "Hello World!" 
    

  3. save and exit the editor program

  4. run the hw program3
    	$ hw [Enter]
    	ksh: hw: cannot execute - Permission denied
    What is the problem here, and how do we fix it?

  5. once the problem above is fixed, we run the script and see the following:
    	$ hw [Enter]
    	Hello World!
You have now written your first successful shell program.

1 In most cases, there is no significant difference between a program, a script, an application, or an executable. These are merely differing names for the same thing. Source languages may differ, program size and complexity may vary, translation methods may be different (e.g. compilation vs. interpretation), but all of these are simply instructions to be executed by a computer.

2 While this is feasible, if a program is going to have this size and complexity, typically a language other than the shell is used; for example Perl, C, or C++.

3 Depending upon your implementation and PATH variable values, you may see an error like hw: not found. If this happens, you may need to execute this as follows: ./hw (that is a dot-slash preceeding the file name). See scenario 1 below.


dot Revisited

We have seen the use of the dot character to be used as a reference to where we currently are in the file system hierarchy. There are additional uses for dot within the shell, with their meaning based upon the contextual usage as follows:
  1. used to specify your current position in the filesystem hierarchy, for example, cp /home/mthomas/foo .

  2. used to specify a file is a hidden file, when used as the first character of the file name, with no trailing space(s). For example, .profile

  3. used as an internal part of the filename. This can be merely a part of the filename, as in rc.local, or an indicator as to what the file contains, for example, foo.cpp (a C++ source file) or a.out. [Wikipedia contributors]

  4. used to specify that a program should be run in the current shell process rather than creating a child, for example, .  user_setup. With white space following the dot, the commands in the program are interpreted as if they were entered into the current shell.

Thus given the following (hypothetical) example, try to identify the meaning of each dot:


From left to right, the first (leftmost) dot has whitespace on its right, and precedes a command, so this is context # 4, specifying that the program .setup.sh be run without spawning a child process. Note the name of this file is .setup.sh, that is dot-setup-dot-sh. The next dot precedes a / indicating this is a relative file specification referencing our current position in the file system, thus context # 1. The third dot has no whitespace following it, thus context # 2, i.e. specifying a hidden file. And the rightmost dot is in the middle of the filename, so this describes a file extension, context # 3.

It is noteworthy here to mention that identical characters, operators, or commands mean different things to different applications. For example, the dot character has a different meaning within the shell than it does to the vi editor application, or than it does to other applications. A user must always remember what application they are interacting with, and to use the language that application understands.


Programs, Processes, and Dots, Oh My!

Now that we know how to create a shell program, we can look at some unique behavior as shell programs execute. For an example, we'll create a simple two line shell script using our editor of choice, naming our program ch_dir, and inserting two lines in the shell program, specifically:
	cd /usr/bin
pwd
The functionality of each command should be apparent, the first changes the working directory to the /usr/bin directory, and the second prints the location of the working directory. As before, to run this program we simply type the name of the program (ch_dir) followed by [Enter]. As we have seen, the shell clones a copy of itself with the fork command, overwrites the clone with the ch_dir process using exec, and begins execution. A diagram of this is below:


As we can see, the child program displays the working directory of /usr/bin when the pwd command is executed, which is what we would expect. The question we must address now is where are we? One might guess that since the pwd command displayed that we were in the /usr/bin directory, that's where we are. But you must keep in mind that the working directory of /usr/bin was set in the child process. When the child process completes, it goes away (as does all of its process space) and control returns to the parent. Therefore, in the parent process, our working directory is the original directory where we executed the ch_dir process.

If we wish to make such changes permanent in our parent process, we must run this shell program without creating a child process. How do we do this you ask? See dot context # 4 above. When using the dot operator in this context, no child process is created and the commands are interpreted by the current shell process. Thus any environmental changes that take place change the current shell environment. Refer to the diagram below.


Shell Program Miscellany

As with all modern programming languages, the shell language provides the capability for comments. Shell comments are initiated using the # character. There is no end comment character, thus everything from the initial # character to the end of the line is part of the comment. Shell comments can be placed in any column on most any line (see paragraph below). Comments do not affect the execution of a shell program, they are simply used to document what the program is (or should be) doing. Do not underestimate the importantce of commenting shell programs (or any programs for that matter). Good programming habits include comments at the start of every program documenting its overall purpose as well as intermediate comments documenting each section or block of a shell program. An example of a real shell comment is the following:
	# Short-Description: Bring up/down networking

One slight variation on the use of the # character is when used in combination with the exclamation character, as #!. This is commonly referred to as the she-bang (derived from sharp-bang). Since not all script commands are portable between all shells, the she-bang gives the programmer a mechanism to specify which interpreter will execute a particular program. To accomplish this, the she-bang must appear as the first two characters (2 bytes/16 bits) on the first line in a shell program. Following the she-bang must be the file specification of the intepreter program which will interpret the script. Thus if the script is a korn shell script, the first line should look like

	#!/bin/ksh
and similarly a bash shell program
	#!/bin/bash
Some modern shells may allow whitespace between the she-bang characters and the interpreter specification, however this is not a standard convention between all shells. Note that the she-bang must be the first two characters in the file and the interpreter specification must be valid for this to work.

Another bit of useful miscellany is the type command. The type command can be used to understand where a command is invoked from, or what type of program it actually is. Is a program a simple shell program, a shell built-in program, a user defined shell program, or a function? Examine the following:

	$ type cd [Enter]
	cd is a shell builtin

	$ type cp [Enter]
	cp is /bin/cp

	$ type ddir [Enter]
	ddir is a function
In the first example, type informs us that the cd command is a command built into the shell; the second example tells the user that cp is a utility that is loaded from the /bin directory; and the third example informs the user that the command ddir is a function, in this case, a user defined function. Some closely related commands are listed in the summary below.

Command Summary




©2019, Mark A. Thomas. All Rights Reserved.