Unix: Redirection

Standard input, output and error

In the previous chapter, you learnt to use the command cat to show the content of a file on the screen. The name of the file to show was given as a command line argument like this:

$ cat unixpast.txt

Now try to run cat without specifying an input file, like this:

$ cat

Then type a few words on the keyboard, and press the [Enter] key. The words you just typed is echoed on the screen. Now hit [Ctrl]-D. This should make the command line prompt reappear.

What happened?

When you run the cat command without specifying a file to read on the command line, it defaults to reading from the standard input (i.e. the keyboard). It keeps on reading line after line, until it receives the character [Ctrl]-D, which in Unix means end of file.

Also, whatever input cat receives is written on the standard output (the terminal). This makes each line appear twice on the screen, because it appears as both input and output.

The command cat, like most standard Unix commands and programs is by default connected to three streams: the standard input, the standard output, and the standard error. Unix programs may of course use more streams of data than these three, but these three are almost universal. By default, the standard input reads from the keyboard, while the standard output and standard error writes to the terminal.

Redirecting the standard output

Having access to standard input, output and error available with nearly every process is handy, because Unix also offer simple mechanisms to redirect all three streams.

To redirect the output of a Unix command, we use the > character followed by the name of the file we want to redirect the input to.

For example, to create a file called dogs1.txt containing a list of dog breeds, you may start cat and then type in the names of some breeds. After typing in a breed, press [Enter] to get to the next line. Terminate input the by typing [Ctrl]-D (end of file).

$ cat > dogs1.txt
spaniel
terrier
dalmatian
[Ctrl]-D

In this case, no duplicate output appears on the screen. The cat command still reads the standard input (the keyboard). But because we've redirected the output, it now goes into the file dogs1.txt instead of to the terminal.

After you get the command line prompt back, you ca check the contents of this file by typing:

$ cat dogs1.txt

The same list of dog breeds as those you typed in should be listed.

Exercise

Using the above method, create another file called dogs2.txt with the following breeds: “, dogo, bulldog, pit-bull, mastiff” (only one breed per line). Use cat to view the contents of dogs2.txt.

Appending to a file

Redirection also let us append text lines to an existing file. This is done by using two characters >> instead of one. So to add more dog breeds to the file dogs1.txt, you should type: something like:

$ cat >> dogs1.txt
beagle
corgi
basset
[Ctrl]-D

To view the contents of the file dogs1.txt on the screen, type:

$ cat dogs1.txt dogs2.txt

You should now have two files. The file dogs1.txt contains six dog breeds, and file dogs2.txt contains four dog breeds. The cat command just keeps reading files that are named on the command line until the contents of all files that have been named have been read.

Concatenating files

The next thing to try out is to use the cat command to concatenate (join together) the contents of dogs1.txt and dogs2.txt into a new file called dogbreeds.txt. To do this, type:

$ cat dogs1.txt dogs2.txt > dogbreeds.txt

What this command does is to read the contents of dogs1.txt and dogs2.txt and at the same time redirect the output to the file named dogbreeds.txt.

To view the contents of the new file, type:

$ cat dogbreeds.txt

It should now be a list of 10 dog breeds.

Redirecting the standard input

The command sort (introduced in the previous chapter) sorts a list alphabetically or numerically.

Like cat, it will by default read from the standard input and write to the standard output. Just to try it out, start it and type in the string “----” (four hyphen) followed by four car names. As always when reading from standard input, you signal end of file by hitting [Ctrl]-D. This is what to type:

$ sort
----
ford
fiat
audi
opel
[Ctrl]-D

As soon as you hit [Ctrl]-D, the program will process the input. In this case, this entails sorting the list typed in alphabetically, and write the following result on the screen:

----
audi
fiat
ford
opel

Now, having to type in input by hand every time we want to sort something is not practical. Instead, to sort the contents of a file, you should redirect input to come from a file instead of from the keyboard.

This is done by using the character < on the command line. For example, to sort the concatenated list of dog breeds, type:

$ sort < dogbreeds.txt

The list of breeds will be alphabetically sorted, and will appear on the terminal .

To output the sorted list to a file instead, redirect the output as well. Type:

$ sort < dogbreeds.txt > sortedlist.txt

To view the contents of sortedlist.txt, use cat, more, or less.

Redirecting the standard error

Now, try to use ls to list something that does not exist. You'll get a similar response to this;

$ ls nosuchthing
ls: cannot access nosuchthing: No such file or directory

Say you want to capture the error message in file name error.log, to document the problem. A first approach is to redirect standard output. Afterwards, you want to use wc to check that the error message is in the log file. Try this:

$ ls nosuchthing > error.log
ls: cannot access nosuchthing: No such file or directory
$ wc error.log
0 0 0  error.log

Unfortunately, this did not work. The error message appeared on the terminal, so it was obviously not redirected, and wc reports that the log is empty.

The reason for failure was that the error message was not written to standard output, but to standard error. This is a stream Unix processes use for error messages as opposed to standard output. To redirect standard output, we need remove the useless log file, then try again, this time redirecting standard error to the file by using 2> on the command line:

$ rm error.log
$ ls nosuchthing 2> error.log
$ wc error.log
1 9 57  error.log

This is much better. We now have 1 line, 9 words and 57 bytes in the log file.

Pipes

Assuming we want to count unique words in the text file named unixpast.txt, the following three lines will get the job done:

$ tr -sc 'A-Za-z' '\n' < unixpast.txt > a.tmp
$ sort -uf < a.tmp > b.tmp
$ wc -w < b.tmp
502

The first line redirects input to come from the file unixpast.txt and translates anything that isn't an ASCII character into a newline, squeezing multiple newlines into a single one (look up the tr manual page if that isn't instantly obvious to you). It redirects the output of all this to a temporary file. The second line redirects the input to read from this temporary file, sorts the stream alphabetically, while removing duplicates and ignoring case. It redirects output to a second temporary file. The last line just counts the words in this second temporary file, reporting that there is 502 of them.

So we have the answer (502 unique words). But the solution is kind of messy, with two temporary files are left behind. What we really want to do is to connect the data streams from these programs together, and not be bothered with temporary files. Unix let you do this, with pipes.

Pipes is how Unix connects the output stream of one program to the input stream of another, sometimes to great effect. Below is an alternative solution to the word counting problem. It is now only a single line, and no temporary files are created:

$ tr -sc 'A-Za-z' '\n' < unixpast.txt | sort -uf | wc -w
502

The symbol for a pipe is the vertical bar (|). put it between two commands on a single line, and the output stream of the first is piped to the input stream of the second.

Exercise

Using grep and sort, display all lines of dogbreeds.txt containing the letter “o”, and sort the result alphabetically.

Summary

Command Meaning
command < file redirect standard input from file
command > file redirect standard output to file
command >> file append standard output to file
cat file1 file2 > file3 concatenate file1 and file2 to create file3
command 2> file redirect standard error to file
command1 | command2 pipe the output of command1 to the input of command2
 
-->