Another way to view a shell is to consider it in the context of a metaphor using its own name. Consider an egg shell. An egg shell is a hard, protective wrapper that encases a growing embryo. Needless to say, UNIX is hardly like a developing fetus, but the wrapper part fits. Figure 1.1-1 depicts the relationship. The UNIX shell can be treated as an operating system wrapper. It encapsulates the devices, applications, files, and communications that in turn access the kernel to perform all of the functions expected of computers. Once again, this is a simplistic explanation of how a user interoperates with a computing system, but it should help demystify the concept of a shell.
As mentioned previously, there are a number of shells to choose from, but it must be noted that there are only two basic types. The first is the Bourne shell. It has a unique syntax different enough from traditional programming languages to require concerted study by those willing to learn it, but it is does have some very familiar constructs that also keep it from being an entirely foreign language. The second type is based upon the C shell. It is named the C shell because its syntax derives from the C programming language. It is in fact similar enough syntactically to allow C programmers to quickly use the branching, looping, and assignment mechanisms. One might easily be able to write if statements and while loops, but the environment is different enough to cause problems if not treated carefully. A user might easily mistake a string for a variable dereference.
From these two shell types came a number of highly-interactive shells. The Korn shell, ksh, and the Bourne Again shell, bash, use Bourne shell syntax. In general, any command useable by the Bourne shell may be used by these others. The C shell's cousin is the tcsh. These derivatives are more user-friendly compared to their parents. They usually provide hot-keys for utilities like file name completion, directory searching, in-line editing, and an extended environment. In some cases, such as the Korn shell, they also allow complex data structures such as arrays. These shells are definitely preferrable for daily use. It is much nicer to hit the tab key and have the shell complete a directory name than it is to have to type the whole thing, especially when the exact name is unclear. And the beautiful part is when something like a loop is needed to allow a command to be repeated, the facilities are right there. Plus, there is often a history buffer that caches recently issued commands so the user does not have to reenter or restructure it. But it is the basics that are explained herein.
This book presents the syntax and use of the Bourne shell. It does so for a very good reason; namely, it is the most widely available of any shell. It is the first shell that came with UNIX. Consequently, it has survived and is distributed with every flavor of UNIX produced today. It can be found in every UNIX as /bin/sh. Because of its availability, any program written that uses it is nearly guaranteed to run on any other UNIX. Of course portability carries only as far as the actual operating system commands allow, but at least the basic programming tools and syntax of the Bourne shell are consistent. From this point forward, any mention of the shell refers to the Bourne shell unless explicitly stated otherwise.
Before diving right into the usual programming bag o'tricks such as variables, functions, if statements, loops, and other fun stuff, this chapter will conclude with some very important syntatical features. Without a thorough understanding of them, it is extremely difficult to read and write shell scripts. For those unfamiliar with the shell or UNIX in general, return to this chapter occassionally and reconsider them in the context of commands given at the prompt or within other shell scripts encountered.
The semicolon allows users to string commands together on a single line as if one command was being issued. Each command within the semicolon separated list is executed by the shell in succession. For example, the line below has two commands joined with a semicolon:
$ cd /var/yp; makeWhen entered, the shell parses the command line up to the first semicolon. The words read to that point are executed. In the example, the current directory is changed to the /var/yp directory. The shell then continues parsing the command line until it reaches the end of the line. The rest of the command is then executed, and as every good UNIX administrator knows, the NIS maps are built as a result of the make that is issued. But this example is not a lesson in system administration. It is a demonstration of how the shell executes each semicolon separated command in the order it is listed after the prompt as if the user had typed the same set of commands on separate lines.
The parentheses, when grouped around a command, cause the command to be executed in a subshell. Any environmental changes occuring within the subshell do not persist beyond its execution; in other words, all changes to the working environment of the commands issued within the parentheses do not affect the working shell. Consider the following example:
$ cd; ls bin etc include man tmp $ cd bin; ls; ls bash gunzip gzip zcat bash gunzip gzip zcat $ cd $ (cd bin; ls); ls bash gunzip gzip zcat bin etc include man tmpThe first command ensures that the home directory is the current working directory and then lists its contents. This is followed by changing directories into bin and then listing its contents twice with the ls command. The next command then returns the shell to the home directory. The last command shows how parentheses behave. Two commands are issued within the scope of the working shell. The first is a compound command delimited by the parentheses. The compound command executes in a subshell. Within this subshell, the current directory is once again set to bin, and its contents are listed. As soon as the subshell terminates, the parent shell resumes control. It lists the contents of the working directory, and as is demonstrated by the resulting output, the shell's current working directory has not been modified by the subshell's directory change.
Like parentheses, curly braces can be used to group a set of commands; however, the commands execute within the context of the current shell. Continuing with the previous example, suppose the last command is reissued with curly braces instead of parentheses.
$ { cd bin; ls; }; ls bash gunzip gzip zcat bash gunzip gzip zcatNotice that this time the bin directory is listed twice. Obviously, the directory change persisted. The shell executed the compound command instead of deferring it to a subshell as it did when parentheses were used. Note also the construction of commands with culry braces. White space precedes and follows the enclosed commands, and a semicolon terminates the last command. The terminating semicolon is required. Certainly, the use of curly braces can be unwieldy:
$ { cd bin; ls }; ls > ^C $ { cd bin; ls }; ls } > ^C $ { cd bin; ls }; ls; } }: No such file or directory bash gunzip gzip zcatThe first command is entered the same way it would be issued if parentheses were used. When entered, the shell responds with the secondary prompt meaning that it expects more input. The job is killed with a control-c, and the command is reentered with a different format. Here, an extra closing brace is appended to the command. The user assumes that the shell was expecting a terminating brace at the end of the line. Unfortunately, the results are the same. After killing the second attempt, the user tries a third time and appends a semicolon to the final ls command. As can be seen, the command becomes syntactically correct, but the results are probably not what the user desires. Focusing on the semicolons reveals what happened. The shell changes directories to bin. Then it tries to list the } directory. The shell interprets the closing brace to be an argument to the ls command. Since no such file exists, it complains. It finishes by listing the current directory.
Hopefully, from this example, the point is clear. Curly braces are, frankly, difficult to use for grouping commands, and when also using culry braces for accessing variable values, code becomes very difficult to follow. It is generally good practice not to use curly braces for command grouping. Use culry braces for variables instead.
The last two conjunctions, && and ||, combine commands conditionally. When the shell encounters either of these, it always executes the first half of the command. Then depending upon its results, the second half may be run. In the case of the double ampersand, the shell handles the second part if and only if the first part exited with a status of zero. This indicates that the first half executed with no errors.
$ cd /usr/bogus && ls /usr/bogus: does not exist $ cd /var/spool && ls calendar cron locks lp mqueue pkgSince /usr/bogus is not a valid directory, the cd command informs the user and returns with a non-zero status. The shell reads the status and, seeing it is non-zero, does not execute a directory listing. On the other hand, /var/spool is a real directory. The cd command, in this case, performs the change and returns with a zero exit status. Again, the shell reads the status, and because it is zero, the shell proceeds with the following command, the ls.
The double vertical bars function the same except that the second half executes if and only if the first half exits with a non-zero status.
$ ls -l total 4 drwxr-xr-x 2 rsayle fvsw 512 Jul 15 14:51 bin drwxr-xr-x 2 rsayle fvsw 512 Jul 15 14:51 man -rw-r--r-- 1 rsayle fvsw 0 Jul 15 14:51 profile $ (ls -l | grep "root") || echo "No files owned by root" No files owned by rootThe first listing above shows that all files in the current directory are owned by rsayle. Hence, the grep for root fails. A non-zero exit code is returned by the subshell that executed the ls and the grep. The parent shell, having encountered ||, acknowledges the result and prints the informational message accordingly.
The input and output displayed on the console comes from three buffers: stdin, stdout, and stderr (pronounced "standard in," "standard out," and "standard error," respectively). These devices are collectively referred to as the standard I/O. Stdin is the terminal's default input. The shell reads commands and answers to prompts from stdin. The default output is stdout. The console prints anything it receives from stdout. In general, this includes print statements from the programs run at the command line and warning messages broadcast by system administrators. Like stdout, the shell also displays the error messages received from stderr on the console, but it is imperative to keep in mind that stderr is not the same as stdout. A good way to remember this is to think of stdin, stdout, and stderr as three different files. In fact, this is exactly what they are. Stdin is file descriptor zero; stdout, one; and stderr, two. As such, each may be referenced by its file descriptor number. Section 8.1, Advanced Shell Topics: More I/O, shows how to manipulate the standard I/O using the file descriptor numbers.
A pipe, represented by a single vertical bar (|), connects the output of its left operand to the input of its right operand. The operands of pipes may be any legal shell command. The goal of piping is to pass output through a series of commands that will massage the data into a more meaningful representation. Normally, the shell parses stdin and passes the input to the command given. The command operates on the input, and in turn, presents the output to stdout. The shell handles pipes as is shown in the figure below. Here, the shell begins as it always does. It parses stdin passing arguments to the command issued. The program runs and provides output. Seeing a pipe, the shell takes the resulting output and passes it as the input to the next command. The next command works on the new input and creates a new ouput which is passed on to the following command in the pipe chain. This continues until the shell reaches the final command. The last command receives the massaged data from the previous command, runs, and then places the result into stdout. The shell the displays stdout to the user.
The most common use of pipes is for filtering. Typically when filtering, a program prints text to stdout which is piped into a utility such as grep, sed, or awk. The utility selects data from the input according to a pattern specified as an argument. The result is usually posted to the screen for visual inspection, but it is possible to save the results in a file, store it as the value of a variable, or repipe it into another command.
$ ypcat passwd | grep "^[a-zA-Z0-9]*::" | cut -f1 -d: jbeadle build35 cwoodrum dborwin jbell dfellbaumThe example above shows how grep and pipes can be used as a filter. The command is actually three programs separated by pipes. The first command, ypcat passwd, displays the network information services (NIS) map for the password database. The shell, as directed by the pipe, sends the map to grep. Grep takes the map and searches for all accounts that do not have a password; this is the filtering operation. Since the goal is to list the accounts, some further processing must be completed so the output is passed to cut. Cut collects the data and trims everything from each line except the account name. The final output is displayed as shown. The figure below depicts the example graphically.
Whereas piping permits the chaining of commands such that the output of one serves as the input of another, redirection can be used to take the input for a program from a file or to place the output of a program into a file. Not surprisingly, these two techniques are known as input redirection and output redirection. Input redirection takes the form: command [input redirect symbol] input. It is equivalent to saying, "Execute a command using the data that follows." Redirecting the output says, "Execute a command and place the printed results into this file," and it has the form: command [output redirect symbol] destination.
$ cat < /etc/defaultdomain fvo.arinc.com $ ypcat hosts > hosts.bak $ cat hosts.bak 127.0.0.1 localhost 144.243.92.13 blatzThe previous commands demonstrate input and output redirection. The first cat command takes as its argument the file /etc/defaultdomain. The argument is given to cat via input redirection, the left brace. Output redirection is shown with ypcat. The ypcat command, when applied to the NIS hosts map, lists all the IP addresses and names of each node in the NIS domain. Here, the output is redirected into the file hosts.bak.
Two types of input redirection exist. The first one uses one left brace. It redirects standard input to be taken from the file specified. For example, if an administrator wanted to know how many accounts were on a particular machine, input redirection of the /etc/passwd file into wc could be used.
$ wc -l < /etc/passwd 12The second type employs a double left brace. Instead of taking the input from a file, the input is taken at the command line between two delimiters.
$ wall << WARNING > "itchy will reboot at 6PM this evening" > WARNING Broadcast Message from rsayle (pts/0) on itchy Wed Jul 24 14:06:57... "itchy will reboot at 6PM this evening"The delimiters may be any word. In this case the word "WARNING" is used. The user here issues the warn all users command, wall. The user also instructs the shell to use as the text of the message the lines that fall between the two WARNING delimiters.
Similarly, there are two types of output redirection. The symbols used are simply inverses of the input redirectors; right braces are used instead of left braces. And of course, the semantics are nearly opposite. In the case of the single right braces, stdout is redireced to a file. The file is created if it does not exist or it is overwritten if the file named already exists and globbing has been enabled (set glob). Below, the shell copies the file combo to the file combo.bak using output redirection. The output from the second cat command is redirected into the new file combo.bak.
$ cat combo 10 23 5 $ cat combo > combo.bak $ cat combo.bak 10 23 5Double right braces have the same effect as single braces except if the file already exists, the output is appended to the file instead of overwriting it.
$ cat combo >> combo.bak $ cat combo.bak 10 23 5 10 23 5Continuing with the previous example, if the user instructs the shell to once again display the contents of the file combo but uses double right braces, then combo.bak ends up with two lines, each being the single line from combo.
As a final I/O basics topic, it is helpful to mention that there are a few generic devices available for redirection besides the standard I/O. Some flavors of UNIX provide devices for the terminal, /dev/tty, the console, /dev/console, and the great bit bucket in the sky, /dev/null. All output sent to /dev/null is simply discarded. It is useful in shell scripts when output should be hidden from the user but should not be stored. There is a subtle difference between the console and the terminal. In a windowed environment, a tty terminal is associated with each shell session; in other words, each command line window such as an xterm is a separate terminal. Redirection to /dev/tty sends output to the active window. The console, on the other hand, is the screen. It is the monitor in general. Most windowing programs provide a special switch to the shell windows that will link /dev/console to the window instead of writing output directly on the screen. In any event, output can be redirected to these devices quite easily.
$ cat /etc/motd >>/dev/tty Sun Microsystems Inc. SunOS 5.4 Generic July 1994 $ cat /etc/motd >>/dev/null $
A regular expression is a sequence of characters specifying a textual pattern. They instruct programs to process only those items that match the pattern. Most regular expressions use metacharacters to express repetition, existence, or ranges of character patterns. It can be very difficult at first to decipher a regular expression that uses metacharacters. Nevertheless, it is extremely important to practice creating and reading regular expressions. It is truly the difference between writing robust scripts and simply browsing around through UNIX.
^$ [a-z][0-1][a-z0-1]* ^<tab>[A-Z][a-zA-Z0-9 ]*$ 10\{3,6\}At first glance, to the novice user, the examples above look like gibberish, but a patient and disciplined user can understand what each pattern means. In the first line, the carat represents the beginning of a line and the dollar sign the end of a line. Together, with nothing in between, they form a pattern describing an empty line. The second example shows how character classes can be used in regular expressions. The pattern represents a line that contains a word beginning with a lower case letter, followed by a zero or one and ending with any number of lower case letters, zero, or one. The square braces denote character classes, and the asterisk is a wildcard meaning any number including zero of the previous pattern. The third example is even more restrictive than the second. It matches patterns beginning with a tab (the tab normally is just typed; the <tab> is shown here for illustrative purposes), followed by an upper case letter, followed by any number of lower case, upper case, digit, or space character, and terminated by the end of the line. It is certainly a complex example, but it shows how by examining the pattern piece by piece, the meaning becomes clear. The last expression matches the number range 103 through 106, inclusive. It specifies a pattern beginning with ten followed by any number in the range three through six.
A list of the metacharacters and their usage is given in the table below.
The five metacharacters at the end are part of an extended set. They only
have meaning in certain programs.
Metacharacter | Usage | Used by |
---|---|---|
|
Matches any single character except newline. | All utilities |
|
Matches zero or more occurrences of the single character that immediately pr ecedes it. The character may be specified by a regular expression. | All utilities |
|
Matches any one of the class of characters enclosed between the brackets. I f a caret (^) is the first character inside the brackets, then the match is reversed. A hyphen is used to specify a range. In order to match a cl ose bracket, it must be the first character in the list. Other metacharacters l ose their special meaning when enclosed within brackets so that they may be matc hed literally. | All utilities |
|
As the first character of a regular expression, matches the beginning of a l ine. | All utilities |
|
As the last character of a regular expression, matches the end of a line. | All utilities |
|
Escapes the metacharater which immediately follows. | All utilities |
|
Matches a range of occurrences of the single character that immediately prec edes the expression. If only m is given, then the pattern matches exactly m repetitions. If m, is specified then at lea st m occurrences are needed for a match. The use of m,n matches any number of repetitions between m and n. | sed, grep, egrep, awk |
|
Matches one or more of the preceding regular expression. | egrep, awk |
|
Matches zero or one instance of the preceding regular expression. | egrep, awk |
|
Matches either the preceding or following regular expression. | egrep, awk |
|
Groups regular expressions. | egrep, awk |
$ ls as*.* ascendmax.alert ascendmax.emerg ascendmax.info ascendmax.warning ascendmax.crit ascendmax.err ascendmax.notice $ cd .. $ ls log/as*.* log/ascendmax.alert log/ascendmax.err log/ascendmax.warning log/ascendmax.crit log/ascendmax.info log/ascendmax.emerg log/ascendmax.noticeAs part of its parsing routine, the shell expands regular expressions before executing the command listed. The expansion acts as if the user had typed a list of files at the command line. In the preceding example, the shell expands the pattern as*.* into all files in the current directory beginning with as and having a period somewhere in the middle. The result is substitued for the pattern and the command is issued to list the files. The second part shows how the shell can be directed to traverse directory trees and perform expansion on subdirectories.
Some of the other common programs that use regular expressions are given
in the table below.
Utility | Purpose | Reference for Further Reading |
---|---|---|
|
Prints each line from its input that matches the pattern specified. | man |
|
Permits line by line editing of its input according to the script of regular expressions and commands given. | man, Sed & Awk (O'Reilly & Associates) |
|
Programming language for extracting and formatting input according to textua l patterns. Works best when the input already has a particular structure. | man, Sed & Awk (O'Reilly & Associates) |
|
Extracts text from the input given a list of field numbers and their field s eparator. | man |
|
Filters out repeated lines from its input so that only unique lines remain i n the output. | man |
|
Sorts the lines of input given. | man |
$ grep "defs.h" *.cpp | cut -f1 -d: | uniq bench.cpp bitvec.cpp bstream.cpp gimp.cpp idendict.cpp match.cpp regexp.cpp rwbag.cpp rwbagit.cpp rwfile.cpp rwtimeio.cpp strngcv.cpp toolmisc.cpp utility.cpp wcsutil.cpp wstring.cppThis command uses files and pipes. The user wants to list all C++ source files that include the header file defs.h. The command begins by searching for the string defs.h in all files ending with the extension .cpp. The files found are passed to cut. Normally, grep prints the file name and every line in a file that contains the pattern. Here, cut is used to trim the lines down to the file name only. The result is in turn passed to uniq which filters out duplicated lines.
Bourne shell quotes come in four forms:
$ echo * bin man profile $ echo "*" * $ echo $USER rsayle $ echo "$USER" rsayle $ echo '$USER' $USERThe first two commands show the difference between using quotes. Special characters are interpreted by the shell when they are not quoted as in the case where the echo command displays the contents of the current directory: bin, man, and profile. The same can be said of the third command in which the value of the environment variable USER is echoed. Moreover, it is important to remember that there are a few special characters that are still interpreted when used with double quotes. Single quotes must be used to perform the escape as is shown by the last few commands.
Double quotes and single quotes are especially useful for patterns containing white space. Suppose, for example, an administrator knows a person's name but can't remember the user's account name. Filtering the passwd database through grep might be a good way to figure out the account name.
$ ypcat passwd | grep Robert Sayle grep: can't open SayleGrep balked on the user's name! Well, not exactly. Due to its syntax, grep tried to find the string "Robert" in the file "Sayle." The trick is to instruct the shell to ignore the white space between the first and last name.
$ ypcat passwd | grep "Robert Sayle" rsayle:hOWajBzikAWRo:328:208:Robert Sayle:/home/rsayle:/bin/tcshThe double quotes tell the shell interpreter to combine all the text contained into one argument, "Robert Sayle." Grep receives the argument and uses it as the key during its search through the output of the ypcat.
To search for quotes within text, single quotes escape double quotes and vice-versa. The following examples illustrate this. The first and third show how one cannot simply use a quotation mark in pattern matching. The shell assumes that more text follows the input. The other examples show how to do it correctly.
$ grep " sloc > ^C $ grep '"' sloc # a given set of files; finds each ";" which denotes an echo "sloc: usage: sloc file1 [file2 ...]" LINES=`grep ";" ${1} | wc -l` echo "${RESULT} lines of code" $ grep ' tgzit > ^C $ grep "'" tgzit # tgzit -- tar's and gzip's the filenames passedAlthough these are simple examples, not enough can be said about the difference between single and double quotes. Double quotes function perfectly in most cases, but there are times when a programmer should use one over another. The realm of possibilities will be explored in succeeding subsections as new topics are presented. For now, a good rule of thumb is to use double quotes in order to group words into a single argument, to allow variable substitution as in "$USER", or to allow command substitution. Single quotes should be used when no substitution should occur. With that said, the discussion of quotes in general continues.
Back quotes are arguably the most powerful form of quoting since they permit a level of nesting called command substitution. In other words, they allow commands to be executed within other commands. When the shell scans a command line and finds an embedded command between back quotes, it runs the nested command. The output of the nested command is then substituted as part of the enclosing command which is subsequently executed. As an example, suppose a programming team places their source files in a common repository.
$ ls -l *.h -r--r--r-- 1 rsayle fvsw 5373 Aug 3 1995 adminlink.h -r--r--r-- 1 rsayle fvsw 5623 Aug 7 1995 agentadmin.h -r--r--r-- 1 rsayle fvsw 4930 Aug 10 1995 agentadminexception.h -rw-r--r-- 1 lshar fvsw 20264 Aug 14 1995 attribute.h -rw-r--r-- 1 lshar fvsw 3346 Aug 14 1995 attributeexception.h -rw-r--r-- 1 lshar fvsw 6819 Aug 14 1995 attributehelper.h -rw-r--r-- 1 lshar fvsw 3424 Aug 14 1995 attributehelperexception.h -rw-r--r-- 1 lshar fvsw 7446 Aug 14 1995 attributereference.h -rw-r--r-- 1 lshar fvsw 3394 Aug 14 1995 attributerefexception.h -rw-r--r-- 1 rsayle fvsw 35012 Aug 16 1995 attributevalue.h -r--r--r-- 1 rsayle fvsw 4959 Jul 25 1995 avdictionary.h -rw-r--r-- 1 lshar fvsw 4851 Aug 17 1995 avexception.h -rw-r--r-- 1 rsayle fvsw 5024 Jul 25 1995 avtype.h -rw-r--r-- 1 lchang fvsw 9106 Jul 19 1995 computeropstatemsg.h -rw-r--r-- 1 346 fvsw 8627 Jul 25 1995 elevation.h -rw-r--r-- 1 346 fvsw 9454 Jul 25 1995 latitude.h -rw-r--r-- 1 rsayle fvsw 5025 Aug 3 1995 linkagentadmin.h -rw-r--r-- 1 lshar fvsw 6260 Jun 19 1995 linkfactory.h -rw-r--r-- 1 rsayle fvsw 4871 Jul 26 1995 linkmibloader.h -rw-r--r-- 1 346 fvsw 9512 Jul 25 1995 longitude.h -rw-r--r-- 1 lshar fvsw 17087 Aug 14 1995 managedobject.h -rw-r--r-- 1 lshar fvsw 14056 Aug 14 1995 mib.h -rw-r--r-- 1 lshar fvsw 3268 Aug 14 1995 mibexception.h -r--r--r-- 1 rsayle fvsw 5263 Aug 2 1995 mibloader.h -r--r--r-- 1 rsayle fvsw 4910 Aug 10 1995 mibloaderexception.h -rw-r--r-- 1 lshar fvsw 3255 Aug 14 1995 moexception.h -rw-r--r-- 1 lshar fvsw 8101 Aug 23 1995 mofactory.h -rw-r--r-- 1 lshar fvsw 3346 Aug 14 1995 mofactoryexception.h -rw-r--r-- 1 lshar fvsw 6134 Aug 14 1995 mofactoryhelper.h -rw-r--r-- 1 lshar fvsw 3424 Aug 14 1995 mofactoryhelperexception.h -rw-r--r-- 1 lchang fvsw 5008 Aug 7 1995 msgsvccbhandler.h -rw-r--r-- 1 lchang fvsw 3232 Aug 7 1995 msgsvccbhdlrexcep.h -rw-r--r-- 1 lchang fvsw 7365 Aug 7 1995 msgsvchandler.h -rw-r--r-- 1 lchang fvsw 3215 Aug 7 1995 msgsvchdlrexcep.h -rw-r--r-- 1 rsayle fvsw 4823 Aug 10 1995 newavexception.h -r--r--r-- 1 rsayle fvsw 3815 Aug 10 1995 nmmsgids.h -rw-r--r-- 1 346 fvsw 9342 Jul 25 1995 operationalstate.h -rw-r--r-- 1 rsayle fvsw 10085 Aug 7 1995 opstatemsg.h -r--r--r-- 1 rsayle fvsw 4790 Aug 10 1995 ovsexception.h -rw-r--r-- 1 lshar fvsw 4174 Aug 14 1995 parserhelper.h -rw-rw-rw- 1 lchang fvsw 3207 Aug 17 1995 regidexception.h -rw-rw-rw- 1 lchang fvsw 9503 Aug 17 1995 regidserver.h -rw-r--r-- 1 346 fvsw 10514 Jul 25 1995 rollablecounter.h -r--r--r-- 1 tbass fvsw 2423 Jul 12 1995 servicedefs.h -r--r--r-- 1 rsayle fvsw 2785 Jul 26 1995 startup.h -r--r--r-- 1 rsayle fvsw 5210 Aug 7 1995 svagentadmin.h -rw-r--r-- 1 lshar fvsw 7909 Aug 17 1995 svfactory.h -r--r--r-- 1 rsayle fvsw 5544 Aug 2 1995 svmibloader.h -r--r--r-- 1 rsayle fvsw 10938 Aug 24 1995 svovshandler.hThe ls command output can be simply piped to grep with the account name, but in order to generalize the script, command substitution could be used.
$ whoami rsayle $ ls -l *.h | grep `whoami` -r--r--r-- 1 rsayle fvsw 5373 Aug 3 1995 adminlink.h -r--r--r-- 1 rsayle fvsw 5623 Aug 7 1995 agentadmin.h -r--r--r-- 1 rsayle fvsw 4930 Aug 10 1995 agentadminexception.h -rw-r--r-- 1 rsayle fvsw 35012 Aug 16 1995 attributevalue.h -r--r--r-- 1 rsayle fvsw 4959 Jul 25 1995 avdictionary.h -rw-r--r-- 1 rsayle fvsw 5024 Jul 25 1995 avtype.h -rw-r--r-- 1 rsayle fvsw 5025 Aug 3 1995 linkagentadmin.h -rw-r--r-- 1 rsayle fvsw 4871 Jul 26 1995 linkmibloader.h -r--r--r-- 1 rsayle fvsw 5263 Aug 2 1995 mibloader.h -r--r--r-- 1 rsayle fvsw 4910 Aug 10 1995 mibloaderexception.h -rw-r--r-- 1 rsayle fvsw 4823 Aug 10 1995 newavexception.h -r--r--r-- 1 rsayle fvsw 3815 Aug 10 1995 nmmsgids.h -rw-r--r-- 1 rsayle fvsw 10085 Aug 7 1995 opstatemsg.h -r--r--r-- 1 rsayle fvsw 4790 Aug 10 1995 ovsexception.h -r--r--r-- 1 rsayle fvsw 2785 Jul 26 1995 startup.h -r--r--r-- 1 rsayle fvsw 5210 Aug 7 1995 svagentadmin.h -r--r--r-- 1 rsayle fvsw 5544 Aug 2 1995 svmibloader.h -r--r--r-- 1 rsayle fvsw 10938 Aug 24 1995 svovshandler.hThe whoami command returns the current account name. When used in the script, the shell executes whoami and substitutes the result for the argument to grep. Hence, anyone using the script would get results specific to their account name.
Finally, the last quoting mechanism is the escape represented by a backslash character, \. It removes the special meaning of the character immediately following. The most common use is for line continuation:
$ /usr/bin/dump 0usbdf 6000 126 54000 /dev/nrst0 \ > /dev/sd0h >> backup.log 2>&1The long backup command above could not fit within one line so an escape was entered before the end of the line. It escaped the newline causing the shell to wait for more input before parsing the command.
Actually, a backslash escapes the special meaning of any character immediately following it. Returning to the first example in which single and double quotes were used to escape metacharacters, a back slash can provide some similar and some not so familiar results.
$ echo *; echo "*"; echo \* bin man profile * * $ echo $USER; echo "$USER"; echo '$USER'; echo \$USER; echo \$$USER rsayle rsayle $USER $USER $rsayle $ echo hello; echo "hello"; echo hell\o hello hello helloAs can be seen, the escape functions most like single quotes, but it is imperative to keep in mind that it does so for exactly one character following the back slash. The last two echo permutations of the USER variable demonstrate this fact. As an aside, the last command string shows how escapes can precede characters that have no hidden meaning to the shell. There is no effect.
For the final example, double quotes and escapes are used to set a variable's value for which the directory list is too long to fit upon one line:
$ DIRS2ARCHIVE=" \ > $HOME/tmp/bin \ > $HOME/tmp/man \ > $HOME/tmp/profile \ > " $ echo $DIRS2ARCHIVE /home/rsayle/tmp/bin /home/rsayle/tmp/man /home/rsayle/tmp/profile