I/O Redirection and the Beauty of the Command Line
CS 321 2007 Lecture, Dr. Lawlor
Say your program reads input from cin, like
#include <string>
#include <iostream>
int main() {
std::string s;
while (std::cin>>s) {
std::cout<<"Read value: "<<s<<"\n";
}
return 0;
}
What if you get tired of typing stuff into a program like this?
Or what if you want to automate the execution of that program to run
10,000 times?
Input Redirection
There's something rather magical you can do in a command-line shell
called "redirection". Any program that reads from standard input
(stdin, cin, or file descriptor 0) can be made to read from a file
instead using the less-than character, like so:
C:\> reads < pile.txt
Read value: Welcome
Read value: to
Read value: the
Read value: pile!
...
You can think of the less-than as "injecting" the file's contents into
the program. This is called "I/O redirection", and it works the
same at a DOS prompt or in a UNIX shell--it's actually a universal of
command-line programs!
On UNIX, you can feed a program a stream of random binary junk with
"someprogram < /dev/urandom". This is actually useful for
security and robustness testing (called "fuzz testing").
Output Redirection
You can also redirect *output* to a file with greater-than:
C:\> dir > sillylist.txt
This stores the directory listing that "dir" prints out to the file
"sillylist.txt". Everything that dir prints (via std::cout,
stdout, or file descriptor 1) will go into the file!
Pipe
You can also redirect one program's output to be the input of another program:
C:> dir | more
This is called a "pipe"--it's
shift-backslash on most keyboards, right under the backspace key.
The output of "dir" is "piped" to the input of the command "more".
All this stuff works in DOS, Windows, or any UNIX, but UNIX machines
have tons of interesting command-line utilities that are designed to
work with I/O redirection. You can get a version of these tools
on a standard Windows machine by installing the package cygwin.
- "wc"
(word count) counts the number of lines, words, and bytes in its
standard input. So "wc < foo.c" counts the number of lines in
the file foo.c, or "ls | wc" counts the number of files in a directory
("ls" is the UNIX equivalent of "dir").
- "grep pattern" searches for occurrences of pattern in its standard input. So "grep fluxor < foo.c" finds all lines in foo.c that contain the string "fluxor".
- "sort"
sorts the lines of its input data alphabetically. So "grep fluxor
< foo.c | sort" sorts the lines that contain fluxor.
- "mail -s subject user@address"
sends off an email. So "grep errors < run_log.txt | mail -s
'Runtime errors' sysadmin@foo.com" emails off a list of errors from the
run.
- "tee file" splits off a copy of the standard input and writes it to file,
but then also forwards it to standard output. This can be used
with an interactive program to take a copy of your input like "tee
myinputs | someprog"(for later automated use with "someprog <
myinputs") , or to store a copy of the program's output to a file with
"someprog | tee progoutputs".
There are hundreds of little utilities like this in UNIX, and a skilled
operator can string them together to get suprisingly useful behavior
with very little effort! Another nice aspect of this particular
program design is that it works great with programs you wrote yourself
using ordinary cin and cout.