Viewing and Redirecting Program Output
In the starter code, you are given a problem.c source code file. This program creates a large 2-D array, fills it with numbers, and prints them out. Fortunately, whoever wrote this was thoughtful enough to write code to check for an out-of-bounds error. Unfortunately, whoever wrote this was not thoughtful enough to debug the program. We’re going to use this program as an example of how the following UNIX commands can be used to parse a lot of program output easily.
Compile and run problem to see exactly what “a lot of program output” means.
$ make problem
$ ./problem
You will notice that the program occassionally prints out an error amidst all the other output of the program. For programs with more errors and more output, you could imagine how we wouldn’t want to keep scrolling up the terminal to read. There are multiple ways in which we can filter out everything but the error messages.
Say less
During the quarter, you have used input redirection (<
) and output redirection (>
) operators to read from and write to files. Let’s first save the output of the program to a text file:
$ ./problem > problem.txt
Note: output redirection (
>
) will replace the contents of the specified file. If you'd rather append to it instead, use>>
.
To view larger files like this, we can use the less
command to be able to scroll through and/or search their contents.
$ less problem.txt
TASK:
- Redirect the output of
./problem
to a fileproblem.txt
- Use
less
to view the contents ofproblem.txt
; try usingj/k
to scroll around. - Hit
h
while inless
to show the help screen, and skim through some of the commands (note:^
refers to the Ctrl key) - Use the help screen to find how to search for a pattern, and how to repeat that search.
- IN YOUR NOTES: Provide a screenshot of the output of
less
with the search results forerror
(it should be highlighted) .
grep
The grep command searches files for lines that match a pattern. grep
takes in two arguments: the string to match and a file to read from.
$ grep error problem.txt
We can also run grep with only one argument, e.g. grep error
, in which case grep will read input from stdin
instead of from a file. This means we can also write an equivalent command using input redirection, like so:
$ grep error < problem.txt
This grep command looks similar to the first, but here "problem.txt" is not passed as an argument to grep; we'll see how this can be useful in part 2 of this lab.
TASK:
- We have provided an additional file named
message.txt
file with more program output, but some lines were written way out of bounds at column index 42. - Use
less
to skim through the contents ofmessage.txt
- Use
grep
to search for all the lines that contain the string42
inmessage.txt
. - IN YOUR NOTES: Provide a screenshot of your search results and the message that was hidden in the out of bounds entries.
Output Streams
In the examples above, we use redirection to manipulate the standard output (stdout
) of a program. By default, printf()
outputs to this stdout
stream, which we illustrate above. What these diagrams do not show is that there actually exists another output stream called standard error (stderr
).
To use stderr
, we use an alternative to printf()
, fprintf()
, and specify that it should output to the stderr
stream. In problem.c
, this line is given to you and commented out. Uncomment this line, and comment the line above (the original print statement). Recompile the program.
TASK: Write your observations for each step in your notes and compare!
- Edit problem.c to print out errors to
stderr
instead ofstdout
and recompile. - Run the program (
$ ./problem
) and observe the output. Do you notice anything different? - Run the program with output redirection to a file (
$ ./problem > problem.txt
) and observe the output. What do you notice? What is the difference between what's in the file and what's on the terminal? - Run the program with output redirection to a file (
$ ./problem 2> errors.txt
) and observe the output. What do you notice?
UNIX uses 1>
implicitly as the default redirection operator when we just use >
. We can read this as “redirect to the stream with file descriptor 1”, where the stream with file descriptor 1 is stdout
. Similarly, 2>
redirects to the stream with file descriptor 2, which is stderr
.
We can also use two redirections in conjuction with each other to redirect different streams to different files in one command.
$ ./problem > problem.txt 2> errors.txt
Or we can redirect both streams to the same file.
$ ./problem > output.txt 2>&1
where this syntax is read as “redirect the stream with file descriptor 2 to the same place as the stream with file descriptor 1”.