The first thing a novice might try is this:
A | B | echo "BEGIN" | C | D
It doesn't work. The echo command is not a filter. Any input to echo command is discarded. Therefore
the program "C" sees the word "BEGIN" but doesn't see the output of program "B," which is lost.
Changing the line to
A | B | echo "BEGIN" ; C | D
also doesn't work. The program "C" doesn't have its input connected to a pipe. Therefore the shell uses
the user's terminal for standard input. The fix is to use one of these forms:
A | B | { echo "BEGIN" ; C;} | D
A | B | (echo "BEGIN" ; C) | D
It takes a while to learn when this is necessary. You have to know which commands read standard
input, and which generate output, and which commands have the flexibility to allow you to do either.
The cat command allows you to specify a hyphen as an option which indicates standard input.
Therefore another way to do the above is:
echo BEGIN >/tmp/begin
A | B | cat /tmp/begin - | C | D
The parenthesis is useful when you want to change a state of the process without affecting the other
processes. For example - changing the current directory. A common method of copying directory trees
can be done with the tar command:
tar cvf . | ( cd newdirectory; tar xfBp - )
Parenthesis can also be used to change terminal permissions. If you have a printer connected to a port
on the computer, and want to change the baud rate of the port to 2400, while printing a file, one
method is to type:
(stty 2400;cat file) >/dev/printer
One of the more powerful uses of these techniques is combining tests with pipes. You see, taking
different branches does not automatically disconnect pipelines. The C shell doesn't do this well, but the
Bourne shell does. Suppose you wanted a filter to read standard input, and if the input contains a
special pattern, you want the filter to add a line before the stream of information. Otherwise, you
wanted to add another line after the input stream. A simple way to do this is to use the shell's features:
#!/bin/sh
tee /tmp/file |
(grep MATCH /tmp/file >/dev/null && cat header - || cat - trailer )
/bin/rm /tmp/file
As you can see, a temporary file is created, and then the grep program searches the copy for a pattern.
If found, the shell output's first the header, then the rest of the input stream. If not found, the stream is
output, and then the trailer is appended. This works because the grep command reads standard reads
standard input if no filename is provided as an argument. However, if a filename is provided, then
standard input is ignored.
Bourne Shell Tutorial
http://www.grymoire.com/Unix/Sh.html
41 of 66
11/21/2011 12:03 PM
I should mention that most people test conditions using the if command instead of the "&&" and "||"
operators. The structure might seem unusual at first, but the more you use it, the more uses you will
find for it.
Bourne Shell Flow Control Commands: If,
While and Until
Some might find the sequence of this tutorials very strange. All these words, and nothing on the if
statement. Strange, but necessary if the basics are properly explained, which is my goal. Too many
tutorials skim over the basics, and people soon find themselves in trouble. Previously I discussed the
status returned by the commands, and introduced the various brackets used for constructing lists.
What's a list? There are three ways commands can be grouped together:
A simple command
A pipeline
A list
A simple command is a collection of words separated by spaces.
1.
A pipeline is a group of simple commands separated by a "|" character. Earlier versions of the
shell used the "^" character instead of the pipe character. Older keyboards didn't have the pipe
character. Newer versions of the shell use both. The exit status of the last command is the status
the pipeline returns.
2.
A list is a series of pipelines, separated by &, ;, &&, or ||, and terminated by a semicolon,
ampersand, or new line character.
3.
A command can either be a simple command, or a complex command. The complex commands can be
one of those listed below:
if list then list fi
if list then list else list fi
if list then list elif list then list fi
if list then list elif list then list elif list then list fi
if list then list elif list then list else list fi
if list then list elif list then list elif list else list fi
while list do list done
until list do list done
The "if" commands can be nested. Therefore the above only lists some of the combinations. Also note
than fi is if backwards.
Commands that must be first on the line
One point that gave me trouble at first with the Bourne shell was this concept of list. It's a simple
concept. The following words must be the first word on a line:
if
then
Bourne Shell Tutorial
http://www.grymoire.com/Unix/Sh.html
42 of 66
11/21/2011 12:03 PM
else
elif
fi
case
esac
for
while
until
do
done
{
}
The line doesn't have to start on the actual beginning of the line. If you have a semicolon or ampersand
before the word, this does the same thing. Let me explain this another way.
One way to write an if
statement is:
if true
then
echo it was true
fi
(The indentation is done for convenience.) The same complex command may also be written:
if true; then echo it was true; fi
Both are equivalent to the description I gave earlier:
if list then list fi
A list is simply a command that ends with a semicolon or ends with a new line character.
The "if" command executes a list if the list after the "if" command is true. A list can have more than
one command. The last one is used to make a decision:
if
false
false
true
then
echo this will print
fi
An "else" list will execute if the test condition is false. Adding an "elif" allows multiple tests. Also, lists
can contain further "if" statements:
if testa; then
echo testa is true
elif testb; then
echo testb is true
else
if testc; then
echo testc is true
else
Bourne Shell Tutorial
http://www.grymoire.com/Unix/Sh.html
43 of 66
11/21/2011 12:03 PM