← back to latest

Skip the header line of a file before you sort or count it

tail -n +2 sales.csv

anatomy

tail
Print the end of a file. With a plain count it measures from the bottom; with a plus sign it measures from the top, which turns it into a tool for dropping the beginning of a file instead of showing the end.
-n +2
Start output at line 2, counting from 1. The number is the first line you keep, not the number of lines you drop, so `+2` skips one line and `+5` skips four. Without the `+`, `-n 2` means the last two lines, which is a completely different command.
sales.csv
Any file. With no file argument tail reads standard input, so the same flag works at any point in a pipeline.

Sample output

2026-01-15,northeast,widget-a,150,4500.00
2026-01-15,southwest,widget-b,89,2670.00
2026-02-01,northeast,widget-a,200,6000.00
2026-02-01,midwest,widget-c,45,2250.00
2026-03-10,southwest,widget-a,175,5250.00
2026-03-10,northeast,widget-b,120,3600.00
2026-04-22,midwest,widget-a,95,2850.00
2026-04-22,southwest,widget-c,60,3000.00

The date,region,product,units,revenue line is gone and every data row is intact. Pipe this into wc -l and you get 8 sales, not 9. Pipe it into sort -t, -k5 -nr and the header no longer lands somewhere in the middle of the numbers.

When you would reach for it

Any time a header row gets in the way of a tool that does not know what a header is: counting records, sorting by a numeric column, summing with awk, or feeding rows into a while read loop. It also works on command output with a title line, like ps, df, or docker ps, when you want only the rows underneath.

Gotchas

  • The count is the first line kept, not the number skipped. tail -n +1 prints the whole file, and so does +0. To drop a three-line preamble, use +4. Off-by-one here silently costs you your first record, so check the first line of output once.
  • tail -n 2 and tail -n +2 do different things by design. On a regular file, plain tail -n 2 seeks to the end and reads backwards, which is why it returns instantly on a 50 GB log. +2 has to read forward from the start through every line it skips, so on a large file it costs a full read.
  • head has no mirror image that works everywhere. GNU head -n -1 drops the last line, but macOS and BusyBox head reject negative counts. For a portable version, use sed '$d'.
  • With more than one file, tail prints a ==> sales.csv <== banner before each one, and those banners end up in your data. Add -q to suppress them, or use the awk variant below.

Variants

$ tail -qn +2 q*.csv > all-quarters.csv

Stitch several CSVs together without repeating their headers. -q drops the per-file banners. Write the output somewhere the glob cannot match, or it reads its own output

$ { IFS= read -r header; echo "$header"; sort -t, -k5 -nr; } < sales.csv

Keep the header on top and sort only the rows. read consumes exactly one line, then sort gets the rest of the same input. (head -n 1; sort) looks equivalent but breaks on a pipe, because head reads a whole buffer and the rows it does not print are gone

$ awk 'FNR > 1' sales.csv returns.csv

FNR restarts at 1 for each file, so this skips the header of every file with no banners and no -q

lineage

tail is old enough that its original syntax is the one POSIX later outlawed. The BSD manual traces it to PWB UNIX in the 1970s, and early versions took the count as a bare argument: `tail +2 file` or `tail -20 file`. POSIX.2 in 1992 introduced `-n` as the portable spelling and kept the old form as obsolescent; POSIX.1-2001 dropped it. GNU coreutils still accepts `tail +2 file` today unless you ask it to follow the newer standard, at which point `+2` becomes a filename. head arrived later, in 3.0BSD, and never had the plus-sign trick, which is why the job of skipping lines from the top fell to the tool named for the bottom.