CS 70

Exploring the Terminal

Work in a pair, at one machine, and take turns driving. The person who is not typing has the more important job: reading what came back and saying what they think it means.

Nothing here can break anything. You are in a scratch directory full of copies. The worst thing that happens is a command complains at you, and a command complaining at you is the main way anybody learns a shell.

Everything below assumes you are logged in to the CS 70 server. If you are not, Using Lab Machines will get you there, and Finding Your Way Around the Terminal is the page to keep open in another window.

1. Make somewhere to make a mess

mkdir ~/scratch

Everything you produce today goes in there. Today is about trying things, and trying things makes junk; ~/scratch is where the junk lives so that ~/cs70 stays for actual coursework. At the end of the semester you can delete the whole directory without having to think about what is in it.

If it tells you the directory already exists, that is fine — you have made it before, and nothing has gone wrong.

2. Get to the playground

cd /cs70/fall2026/lab/week01lab2
ls

The shell is always somewhere. It keeps track of one directory — your current directory — and remembers it between commands. Nothing on screen represents it the way a window or a highlighted folder would, which is why it is easy to forget it exists, but it quietly changes what your commands mean.

ls with nothing after it means "list what is here". cat oliver.txt means "the oliver.txt that is here". cd is how you go somewhere else, pwd says where you ended up, and the ~ SERVER > prompt is showing you the same thing all along.

So when a command says "no such file" about a file you are certain exists, the answer is almost always that you are standing somewhere else. pwd settles it in one keystroke's worth of typing.

There is a chunk of text about Oliver Twist in here, and the source code for the three small programs you are about to meet.

  • Hedgehog speaking

    I looked inside meow.c and it is not C++. I do not know any C!

  • LHS Cow speaking

    You do not need to. Today these are programs you run, not programs you have to read, and you will meet C properly in CS 105.

  • Bjarne speaking

    C is part of our heritage! Almost everything in that file is perfectly good C++ as well.

  • LHS Cow speaking

    And shuffle.cpp in the same directory is C++, and is written to be read, so start there if you want to look at one properly.

You cannot damage this directory, and you do not own it. If you want to change one of these files, copy it to your scratch directory first:

cp oliver.txt ~/scratch/

3. Look at a file three different ways

cat oliver.txt
wc oliver.txt
less oliver.txt

less takes over the screen. Press q to get out.

  • Hedgehog speaking

    It has taken over the whole terminal and I do not know how to leave.

  • LHS Cow speaking

    q. That is the whole answer, and it works in most things that take over the screen this way.

Talk about it: cat and less both show you a file. When would you actually want one rather than the other? What did wc give you three numbers for?

4. Meet two new commands

Run this, exactly as written, with nothing after it:

meow

It sits there. It has not crashed and it is not stuck — it is waiting for input, because that is what this kind of program does. Type a sentence and press Return. Then press Control D on a line of its own to tell it you are finished.

Now try shout the same way.

Both of these are filters. A filter reads from its standard input, does one small thing, and writes to its standard output. sort, grep, wc and tr are all filters too. Once you have seen one, you have seen the shape of a great many Unix commands.

Standard input and standard output are just names for where a program reads and writes when nobody has said otherwise. By default they are both your terminal: standard input is your keyboard, standard output is your screen. Everything in the next two sections is the shell quietly pointing them somewhere else, without the program ever finding out.

  • Duck speaking

    Why would anyone write a program that just waits like that?

  • LHS Cow speaking

    Because it makes the program useful in situations its author never thought about.

  • RHS Cow speaking

    meow does not know whether its input is you typing, a file, or another program; it does not have to, and that is the whole trick.

Talk about it: before you run the next section, guess what meow will do to a number like 1838, and to punctuation. Then find out.

5. Send a file in, and send the result somewhere

The shell can connect a program's input and output to files, using < and >:

meow < oliver.txt
shout < oliver.txt
shout < oliver.txt > ~/scratch/loud.txt
cat ~/scratch/loud.txt

> overwrites without asking. If the file already exists, its old contents are gone. This is one of the two or three genuinely sharp edges in a shell, and it is worth meeting it here rather than on your homework.

Talk about it: shout < oliver.txt > loud.txt mentions two files and the program mentions neither. Where does shout think its input is coming from?

6. Pipes: joining programs together

A pipe, |, takes the output of one program and makes it the input of the next. Nothing is written to disk in between.

grep -i oliver oliver.txt | meow

Now try both of these, and look carefully at the difference:

shout < oliver.txt | meow
meow  < oliver.txt | shout

Talk about it: one gives quiet meows and one gives loud ones, and neither program is behaving inconsistently. What has happened to the capital letters in the first case? Can you say, in one sentence, why order matters in a pipeline?

  • Cat speaking

    In the first one, meow throws the letters away before shout ever sees them.

  • LHS Cow speaking

    Exactly. Each stage only ever sees what the stage before it produced, which is the whole idea and also the whole hazard.

There are three streams, not two

Every program starts life with three streams, not two. Alongside standard input and standard output there is standard error, which is where a program sends its complaints.

They are separate on purpose, and you can see why in one command. Ask ls for a file that does not exist, and send it through shout:

ls nosuchfile.txt | shout

The error message comes back in lower case. shout never touched it, because it never went down the pipe — it went straight to your screen, past shout entirely.

That is the point of having a third stream. If errors travelled with the data, then every pipeline would quietly poison the next stage with its own complaints, and wc -l would happily count your error messages as lines of output.

If you actually want the error in the pipe, you can say so:

ls nosuchfile.txt 2>&1 | shout

Now it is SHOUTED, because 2>&1 means "send stream 2 to wherever stream 1 is going". The streams are numbered: 0 is standard input, 1 is standard output, 2 is standard error, and those numbers are why redirection looks the way it does. Plain > is really 1>; nobody types the 1.

The same split explains something you will meet on your homework:

ls oliver.txt nosuchfile.txt > ~/scratch/mylist.txt

The error still appears on your screen even though you redirected the output, and ~/scratch/mylist.txt contains only the part that worked.

And here is the thing worth taking away. Look at meow.c in this directory. It is nineteen lines, and there is nothing in it about files, or pipes, or terminals — it just reads characters until there are no more. The program never learns whether it was talking to a keyboard, a file, or another program. The shell arranges all of that before the program starts, and that arrangement is what lets nineteen lines be useful in situations its author never imagined.

7. A third filter, and a pipeline built one stage at a time

Meet shuffle. It puts the lines of its input in a random order.

shuffle < oliver.txt

shuffle is a filter like the other two, but it differs in one important way, and it is worth seeing why. meow and shout handle one character at a time and never need to remember anything. shuffle cannot do that: to put the last line first, you have to have read the last line. So it reads the whole input before it writes any of it.

Now a real question — what does a passphrase made of four random English words look like? Do not run the finished pipeline. Build it, and after each stage say out loud what changed.

shuffle < /usr/share/dict/words | head -4
shuffle < /usr/share/dict/words | head -4 | tr '\n' '-'

Run the first one a few times. You get different words each time, and a different shuffle: seed line with them.

That seed line is on standard error, which you met in the deeper dive above. Look at what that buys you: the seed is on your screen, where you can read it, and it is not in the pipe, so head -4 never sees it and it cannot end up in the middle of your passphrase.

Homework 0 asked you to pick a strong password and not something like goat42. Four random words is one of the standard ways people build one that is both strong and memorable — so this pipeline is a toy, but it is a toy version of a real tool.

Now give it the seed instead of letting it choose:

shuffle 1234 < /usr/share/dict/words | head -4
shuffle 1234 < /usr/share/dict/words | head -4

The same four words, every time, for as long as that file does not change.

Talk about it: a shuffle that always gives the same answer sounds useless. When would you actually want one? (Think about two people trying to compare results, or about testing a program that is supposed to behave randomly.)

And then: what happened to the last newline in the tr version, and can you tell from looking at the screen?

8. The manual is on the machine

Every one of these commands can explain itself:

man shuffle
man wc

Man pages are paged with less, so q gets you out, and / searches.

Try this: using only man sort, work out what sort -u does, and confirm it by trying it. Nobody is going to tell you the answer; finding it is the exercise.

  • Goat speaking

    Meh. I would just search the web.

  • LHS Cow speaking

    Often faster, genuinely. But the manual on this machine describes this version of the command, and the web describes some version on some machine somewhere.

  • Rabbit speaking

    There are eight numbered sections, and man 3 printf is a different page from man 1 printf

  • LHS Cow speaking

    Which is real, and is exactly the kind of thing worth knowing on about week nine rather than today.

9. Things to try

Pick whichever appeal. Compare notes with the pair next to you.

  • First letters. What letter do most English words begin with? cut -c1 will take the first character of every line, and then you want the same sort | uniq -c | sort -rn idea as before. While you are there: why does uniq need its input sorted first, and does leaving the sort out look like an error or like a wrong answer?
  • A random line, and a random sample. shuffle | head -1 gives you one random line of anything. What else can you point it at? (/etc/passwd and oliver.txt are both on this machine.)
  • Word frequencies. What are the ten most common words in oliver.txt? You will want tr -cs 'A-Za-z' '\n' to get one word per line, and then the same sort | uniq -c | sort -rn idea as before.
  • Spoilers. oliver.txt has a line beginning SPOILER. Show the file without it. (grep -v reverses a match.)
  • Odd words. Find every word in /usr/share/dict/words containing a double u. Then count the ones starting with q that are not followed by u — there are fewer than you would think, and one of them is barely a word at all.
  • Three-letter words. grep "^...$" will do it, and working out why is more interesting than the answer.
  • How long is a dictionary? And how many of its words does meow reduce to exactly the same thing?

You will want grep -v, -i, and -f to read the patterns from the dictionary. You will also need to tell grep that a dictionary word has to match the whole line rather than appear somewhere in it — and if your pipeline confidently returns nothing at all, that is the flag you are missing. A pipeline that returns nothing looks exactly like a pipeline that found nothing, which is a lesson worth having on a day when the right answer is known.

If you want to keep going after lab

Not a lab activity — this one is for later, if you enjoyed yourself.

Rewrite meow and shout in C++. Both are short, both are in week01lab2 for you to read, and shuffle.cpp sitting next to them is a worked example of the same job done in C++ — so you have a model to copy the shape from. std::cin.get() and std::cout.put() will do what getchar and putchar do, and std::cin >> ... is not what you want here, which is itself worth finding out the hard way.

When you have built one, put it in ~/scratch and use it in a pipeline exactly as you used ours. A program you wrote is a first-class citizen of the shell — nothing special had to be arranged, and the shell cannot tell the difference. That is a surprisingly good feeling, and it is most of the reason this activity exists.

Before you leave

Find the most surprising thing you learned and tell somebody else in the room about it. Then put it in your lab check-in, in the box that asks what you got up to — that is how it reaches us, and how it reaches next year's version of this page.

Why is it called less?

Because there was already one called more.

more came first. It shows you a screenful of a file, prints a prompt at the bottom, and waits — the prompt being where the name comes from, since it is essentially asking whether you would like some more. Try it on something long:

more /usr/share/dict/words

more had one large limitation: it only went forwards. Once a line had scrolled past, it was gone, and you started again from the top.

less was written as a better more — it can scroll backwards, search, and jump around. Which left its author with a program that did more than more and needed a name, and the name its author chose was the joke you would choose too.

You do not have to take our word for any of this. It is in the manual:

man less

The NAME section reads, in full: less - opposite of more.

  • Goat speaking

    Meh. Is the whole system like this?

  • LHS Cow speaking

    A fair amount of it, yes. awk is three surnames, grep is an editor command that escaped, and tar is short for “tape archive” on machines that have not had tapes for forty years.

  • Rabbit speaking

    And less is now so much better than more that on many systems more is less, running in a more-compatible mode—

  • LHS Cow speaking

    Which is a lovely sentence and we are going to leave it right there.

One practical thing falls out of this. man uses less to show you its pages, which is why q gets you out of a manual page, and why / searches inside one. Learning one pager teaches you the other.

(When logged in, completion status appears here.)