cat, head and tail for Testers
Ubiquitous commandline tools. Text unfinished, still useful.
These three small tools give you back what you give them, line by line. head gives you the top 10 lines, tail gives you the bottom 10 lines. And cat gives you all the lines in a great waterfall of stuff. We'll come back to cat.
The context these are initially useful in is the context of handling text files without a neatly scrollable editor, and possibly without the memory to actually hold the whole file. Want to see what's in my_vast_list.csv? Use head my_vast_list.csv and you can get a clue without reading the whole thing. Want to see the most recent lines glommed on the end of awful.log? Use tail awful.log and there they are without needing to load the file or scroll through it.
You can use cat this.txt that.txt to lump two files together: cat is made to concatenate files. But you'll more often find it used in the wild with just one input. That's because concatenating a file with nothing does nothing to the file, so cat hands it back to you, line by line – the file has become a stream.
Streams are managed line-by-line, which is memory-efficient. Work scales with more time, rather than more hardware. Unix is very stream-y – all the connections between tiny tools with |and > are built on streams. cat opens the door to processing files with the power of those commandline tools.
cat to stream
I keep rewriting this – the basic summary is that cat is handy enough to be used all over the place, but that plenty of that use is not necessary good use!
cat's broad usefulness is that it allows something to be processed line by line. Why write for i = 0 to lines_in(file); do something to line(i); append that to output if you can write cat file | something > output.
However, this ubiquitousness also leads to Useless Use of cat. You might instead write something < file >> output.
Here are some examples:
- use
cat myresults.md | grep "FAIL"to pick out all the lines marked "fail" (or usegrep "FAIL" myresults.md - use
cat myresults.md | grep "FAIL" > fails.mdto put those lines in a file - use
cat test_whatever.py | grep "test_"to pick out
also
- use
cat stuff > /tmp/before.txtto copy a file - AVOID THIS: To swiftly create a file without bothering with an editor,
cat > myfile.txt, type away (you're putting a stream intocatand it's streaming it into a file) and hit ctrl+d when done. It's neat, but it overwritesmyfile.textirrevocably and I've kicked myself for doing it. However... catis a regularly used in "here-documents", which I see more commonly using LLMs than I did in my time doing ops and environments. Look out forcat <<EOF > something, followed by lines with anEOFat the end.
cat tricks
While it has less-used features that can be very handy,
- Number the lines with
cat -n boogaloo.txt. Number just the non-blank ones withcat -b boogaloo.txt. - Skip multiple blank lines with
cat -s boogaloo.txt– and you can combine that with-nor-bfor counts. - Some characters found in files can't be displayed in a terminal – at best your days gets choppy or beepy, at worst your setup is changed in an unspeakable and unfindable way. Pipe output like this
nastyout | cat -tveto make it sane. - Split that up to look for invisible problems in text. In
cat -tveabove,-tshows tabs as^I,-eshows line-ends as$and-vshows non-printing characters in a printable (but obscure) way. Use it to spot places where tabs should be spaces, where line-ends aren't where you want, or where some sossidge has furkled your data. catis made to concatenate files. It does the job well. Use it ascat me.txt you.txtto fill your terminal, or pipe it somewhere usefulcat us.txt them.txt > everyone.txt
Using and abusing cat
catregularly pours gigabytes of text into my terminal, typically when I don't know what I'm looking at.cat whatever | lesslets me step through it using thelesspaginator. Or I could justless whatever, though by some unthinking habit I usecat.
Shared behaviours
- You can use
cat,headandtailin combination with others, as input or as output. - You can use all these with file patterns
head -n 3 *will show you the tops of all the files (with a==> filename <==at the top of each).tail -n 5 *.logwill show you the last5lines in each file in the local directory ending.log
cat *.logwill show the contents of all the files ending.log. If they all have the same serial info at the start, you can at a pinchcat *.log | sortto interleave them.
Shared with head and tail
- You can set the size of
headandtailwith-n, sohead -n 5 awful.logshows the top 5 lines ofawful.log. Both default to 10. - You can limit the number of characters output with
-c: handy if producing something of a particular size. - You can use
-quiet to never show filenames (handy if passing to processing) and-verbose to always show filenames (handy if reading and confused)
Back to head and tail
- You can aim the output of something into the input of one of these. If
grep diddley doo.txtshows all lines containingdiddley, thengrep diddley doo.txt | head -n 5shows the first 5diddleys. - You can
tail -fa log – and as the log grows (from the bottom, naturally), that changes what you see.-fis for follow. Use-Fto retry: good when the file doesn't exist, or may vanish. - Want to work with something of a particular size? Use
head/tailwith-nfor number of lines,-cfor number of characters. - Want something inbetween two limits? Use
head -n 10 | tail -n 3to see lines 7 to 10. More precise – want just line 453?head -n 453 | tail -n 1 - Just want to see the start of somecommand? Throw it to head
somecommand | head. Just want the last lines of message? Throw it to tailsomecommand | tail(?jlcheck this for long output). This gets used withcurlandwgetwhen grabbing stuff from a URL, with SQL commands when one hasn't bothered to restrict the size of the output, and more.
Oddnesses
Careful of using -n vs -somenumber i.e. head -n 3 and head -3. They do the same, but -3 is considered obsolete.
Note that head -3 and head -n 3 may do the same thing, but head -n +3 and head +3 don't do the same things – on some systems I believe that head -n +3 starts from the third line... on my Mac with a bash shell, it doesn't. I don't know how to do (say) "from the 5th line to the end" without piping a head to a tail and that means knowing the file length.
Note that the man for tail says "-n +NUM to skip NUM-1 lines at the start". I don't know what this means – at the start of what; the input file, or the result.
man pages



because it does a simple thing: it writes out what you give it.
If you give it one file, it lists it back for you.
Think of these tools as acting on something as rows – you can give each a file, a bunch of files, or a stream.
catshows (and can transform) all the rows, in order.headshows rows from the top, in ordertailshows rows from the bottom, in order
That's kind-of obvious for files, and that's not the only way to think of these tools.