Photo by Veronica Reverse / Unsplash

cat, head and tail for Testers

Tools Jun 19, 2025 (Jun 19, 2025) Loading...

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 use grep "FAIL" myresults.md
  • use cat myresults.md | grep "FAIL" > fails.md to put those lines in a file
  • use cat test_whatever.py | grep "test_" to pick out

also

  • use cat stuff > /tmp/before.txt to 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 into cat and it's streaming it into a file) and hit ctrl+d when done. It's neat, but it overwrites myfile.text irrevocably and I've kicked myself for doing it. However...
  • cat is 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 for cat <<EOF > something, followed by lines with an EOF at 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 with cat -b boogaloo.txt.
  • Skip multiple blank lines with cat -s boogaloo.txt – and you can combine that with -n or -b for 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 -tve to make it sane.
  • Split that up to look for invisible problems in text. In cat -tve above, -t shows tabs as ^I, -e shows line-ends as $ and -v shows 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.
  • cat is made to concatenate files. It does the job well. Use it as cat me.txt you.txt to fill your terminal, or pipe it somewhere useful cat us.txt them.txt > everyone.txt

Using and abusing cat

  • cat regularly pours gigabytes of text into my terminal, typically when I don't know what I'm looking at. cat whatever | less lets me step through it using the less paginator. Or I could just less whatever, though by some unthinking habit I use cat.

Shared behaviours

  • You can use cat, head and tail in 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 *.log will show you the last 5 lines in each file in the local directory ending .log
    • cat *.log will show the contents of all the files ending .log. If they all have the same serial info at the start, you can at a pinch cat *.log | sort to interleave them.

Shared with head and tail

  • You can set the size of head and tail with -n, so head -n 5 awful.log shows the top 5 lines of awful.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.txt shows all lines containing diddley, then grep diddley doo.txt | head -n 5 shows the first 5 diddleys.
  • You can tail -f a log – and as the log grows (from the bottom, naturally), that changes what you see. -f is for follow. Use -F to retry: good when the file doesn't exist, or may vanish.
  • Want to work with something of a particular size? Use head/tail with -n for number of lines, -c for number of characters.
  • Want something inbetween two limits? Use head -n 10 | tail -n 3 to 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 tail somecommand | tail(?jlcheck this for long output). This gets used with curl and wget when 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

cat(1) - Linux manual page
head(1) - Linux manual page
tail(1) - Linux manual page


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.

  • cat shows (and can transform) all the rows, in order.
  • head shows rows from the top, in order
  • tail shows 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.

Member reactions

Reactions are loading...

Sign in to leave reactions on posts

Tags

Comments

Sign in or become a Workroom Productions member to read and leave comments.

James Lyndsay

Getting better at software testing. Singing in Bulgarian. Staying in. Going out. Listening. Talking. Writing. Making.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.