Many programs available to the UNIX or Linux operating systems have the ability to process input from standard input through a pipe, Similarly, the following short script demonstrates how to perform this task using a shell script. The script also demonstrates a number of interesting techniques that are covered in more depth elsewhere in this book.
The purpose of the script is to take its input, whether from a file or stdin
, and print the data to stdout
in reverse order. The script provides two ways to perform this task. The first method works by calling the script with an argument that is the name of the file to be reversed. Alternatively, you can pipe output from some other command into the script, and it will then perform the reversal on its standard input.
First we initialize a counter variable that will track the lines of input the script receives.
#!/bin/sh
counter=0
Next we define the populate()
function.
populate () {
counter=$(($counter+1))
eval COUNT${counter}='$LINE'
}
This function creates a new variable by calling the eval
command. The eval
command performs variable and metacharacter expansion in what follows before passing the result to the shell to evaluate; it is used here to construct a line of code (an assignment statement) out of the contents of preexisting variables. This is a fairly powerful method for creating command lines at runtime and is explained in more detail in Chapter 7.
The populate()
function first increments the counter variable and then creates a variable called COUNTnn
where nn is the value of the counter that in this case refers to the specific line of input to be reversed. The collection of these variables behaves analogously to an array, except that the limit on the number of elements is based on the amount of memory the shell makes available rather than the preset limit for the shell's array construct.1
__________
1. The maximum number of elements in an array in ksh88
is 1024. In ksh93
, it is increased to 4096. bash
has no range limit.
Now we check to see whether a filename has been passed to the script.
if [ "$1" != "" ]
then
if [ -f $1 ]
then
while read LINE
do
populate
done < $1
else
echo "$1 Does not exist, please try again"
fi
If it has, the script tests whether the file exists. If the file is validated, the script redirects it into the back end of the while
loop. This loop calls the populate
function for each line of the file. If the file named in the command line doesn't exist, we just output a simple error message.
You may think performing a cat of the file and piping the result into the front of the while
loop would work, but this can have unforeseen results. It works in ksh
without a hitch, but not in pdksh
or in bash
. (For more information about piping data into a while
loop, see Chapter 10.) Redirecting the file into the back end of the while
loop always works.
If no filename was passed to the script from the command line, we assume the data is being piped into the script.
else
while read LINE
do
populate
done
fi
Each line is read in sequence, and the populate
function is called to expand the variables to their values.
Once the script has finished processing all the data, the counter
variable contains the number of the last line of data that was processed.
while [ $counter -gt 0 ]
do
eval echo '$COUNT'$counter
counter=$(($counter-1))
done
This while
loop then counts down from the counter
value, decrementing by one each time, outputting the contents of each of the newly created variables. Thus we reverse the order of the lines from the input.
3.143.22.26