TOMMY: In this video, we'll learn about
redirecting and pipes.
So far, we've been using functions like printf to
output data to the terminal and functions like GetString
to allow the user to provide input to our
program using the keyboard.
Let's quickly take a look at a program that gets a line of
input from the user and then outputs it.
>> On line 7, we're prompting the user for a string, and
then on line 8, we're printing it back out.
Let's compile and run our program.
Great.
The string we provided was echoed back
to us at the terminal.
This happened because the printf function wrote to a
stream called standard out, or s-t-d-out.
When something is written to stdout, it's by default
displayed by the terminal.
>> So that's all well and good, but what if, instead of simply
displaying the string, we wanted to save it to a file?
For example, we might want to remember exactly what our
program did when we gave it a particular input later.
One approach would be to do this in our C program, using
some special functions for writing to files that we'll
see in another video.
Even easier, though, would be to somehow
redirect stdout to a file.
That way, when printf writes to stdout, the contents will
be written to a file rather than
displayed by the terminal.
We can do just that by adding a greater-than sign, followed
by a file name, to the command we use to execute our program.
>> So, rather than simply executing ./redirect, we can
run ./redirect, followed by a greater than sign, followed by
filename, like file.txt.
Let's see what happens.
OK.
Notice that this time, nothing was displayed at the terminal,
but we haven't modified the contents of our
C program at all.
Let's now examine the contents of this directory with ls.
>> All right.
We now have a new file in our directory called file.txt,
which is the file name we supplied when we ran our
Redirect program.
Let's open up file.txt.
And here, we can see that the stdout out of redirect was
written to the file called file.txt.
So let's run the previous command again, but supplying a
different input this time.
Okay.
Let's take a look at file.txt now.
>> We can see here that the file has been overwritten, so our
original input isn't there anymore.
If we instead want to append to this file, putting the new
input below the existing contents of the file, we can
use two greater-than signs instead of just one.
Let's try that.
Now, if we open file.txt again, we can see both of our
input lines.
In some cases, we might want to discard any
output of our program.
Rather than writing the output to a file and then deleting
the file when we're done with it, we can write a to special
file called /dev/null.
When anything is written to /dev/null--
or just devnull for short--
it is automatically discarded.
So think of devnull as a black hole for your data.
>> So now we've seen how the greater than sign can redirect
stdout, let's see how we can redirect standard in--
or s-t-d-in--
the analogue of stdout.
While functions like printf write to the stream called
stdout, GetString and similar functions read from the stream
called stdin, which, by default, is the stream of
characters typed at the keyboard.
We can redirect stdin using the less than sign, followed
by a filename.
Now, rather than prompting the user for input at the
terminal, a program will open the file we specified and use
its lines as input.
>> Let's see what happens.
Great.
The first line of file.txt has been printed to the terminal
because we're calling GetString once.
If we had another call to GetString in our program, the
next line of file.txt would have been used as
input to that call.
Again, we haven't modified our C program at all.
We're only changing how we run it.
And also remember, we haven't redirected stdout this time,
so the output of the program was still
displayed at the terminal.
We can, of course, redirect both stdin
and stdout like this.
Now, file2.txt contains the first line of file.txt.
>> So, using these operators, we've been able to read and
write from text files.
Now, let's see how we can use the output of one program as
the input to another program.
So here's another simple C program I
have here called hello.c.
As you can see, this simply outputs "Hi
there!" to the user.
If I want redirect to use as input the output of hello--
another program--
I could first redirect the stdout of hello to a file called
input.txt, then redirect the stdin of redirect to that same
file--input.txt.
So I can do ./hello > input.txt.
Press Enter to execute this.
Followed by ./redirect <
input.txt, and execute that.
So we can shorten this a bit with a semicolon, which allows
us to run two or more commands on the same line.
So I can say, ./hello > input.txt, semicolon,
./redirect < input.txt.
>> So this works, but it still feels pretty inelegant.
I mean, do we really need this intermediary text file that's
no longer necessary after redirect runs?
Luckily, we can avoid this extra text file using what's
called a pipe.
If I say, ./hello | ./redirect, then the stdout of
the program on the left--
in this case, hello--
will be used as the standard input for the
program on the right.
In this case, redirect. So let's run this.
>> There we go.
We can see that the output of hello was used as the input
for redirect.
By stringing commands together using pipes, we form what's
called a pipeline, since our output is essentially moving
through a sequence of commands.
Using pipes, we can do some cool stuff without needing to
write any code at all.
For example, let's say we want to know how many files are
inside of this directory.
Using a pipe, we can combine the ls command with the wc--
or wordcount--
command.
Ls will output each file in the directory to stdout, and
wc will tell us how many lines were given to it via stdin.
So, if we say ls | wc -l--
supplying the -l flag to wc to tell it to count lines--
we can see exactly how many files are
in the current directory.
>> So let's take a look at one more example.
I have here a file called students.txt,
with a list of names.
However, these names aren't in any order it all, and it looks
like a few names are repeated.
What we want is a list of unique names in alphabetical
order, saved to a file called final.txt.
We could, of course, write a C program to do this for us.
But it's going to get unnecessarily
complex pretty quickly.
Let's instead use pipes and some built-in-tools to solve
this problem.
>> The first thing we need to do is read the file students.txt.
The cat command will do just that.
It will read in the specified file and write
its contents to stdout.
After we've read the text file, we'll
want to sort the names.
The sort command can handle this for us.
Sort will output the line supplied via stdin to stdout
in sorted order.
In order to supply the contents of students.txt to
sort's stdin, we could use a pipe to combine cat and sort.
So I can execute cat students.txt | sort and
press Enter.
And now, we see the contents of students.txt in
alphabetical order.
>> So let's add another command--
uniq, or unique--
to our pipeline.
As you might guess, uniq, when supplied a sorted sequence of
lines via stdin, will output the unique lines.
So now we have cat students.txt
| sort | uniq.
Finally, we can save the output of the pipeline to a
file via cat students.txt | sort | uniq
> final.txt.
So, if we open up final.txt, we have exactly what we were
looking for:
a list of unique names in alphabetical order,
saved to a text file.
By the way, we also could have said sort <
students.txt | uniq > final.txt to do exactly
the same thing, using each of the operators we've seen in
this video.
>> My name is Tommy, and this is CS50.