93

How can I discard the last n lines of a file with a unix command line filter?

That would be sort of the opposite of tail: tail discards the first n lines but pipes the rest through, but I want the command to pipe everything through except the last n lines.

Unfortunately I haven't found anything like that - head doesnt help, too. EDIT: At least in Solaris it does not take negative arguments.

Update: I'm mostly interested in a solution that works for big files, i.e. logfiles, where you might want to inspect what happened except in the last minutes.

6 Answers6

87

If you have GNU head, you can use

head -n -5 file.txt

to print all but the last 5 lines of file.txt.

If head -n takes no negative arguments, try

head -n $(( $(wc -l file.txt | awk '{print $1}') - 5 )) file.txt
slhck
  • 235,242
40
head file.txt               # first 10 lines
tail file.txt               # last 10 lines
head -n 20 file.txt         # first 20 lines
tail -n 20 file.txt         # last 20 lines
head -20 file.txt           # first 20 lines
tail -20 file.txt           # last 20 lines
head -n -5 file.txt         # all lines except the 5 last
tail -n +5 file.txt         # all lines except the 4 first, starts at line 5
Kjetil S.
  • 609
16

On MAC the solution of getting all lines except the last N lines:

head -n -5 file.txt

will not work, as you will get the following error:

head: illegal line count -- -5

One of the solutions is installing coreutils and running ghead:

brew install coreutils
ghead -n -5 file.txt

The GNU Core Utilities or coreutils is a package of GNU software containing reimplementations for many of the basic tools, such as cat, ls, and rm, which are used on Unix-like operating systems.

MikeL
  • 261
13

Here's a simple way to delete the last line, which works on BSD, etc.

sed '$d' input.txt

The expression reads "on the last line, delete it". The other lines will be printed, since that is sed's default behavior.

You could chain them together to remove multiple lines

sed '$d' input.txt | sed '$d' | sed '$d'

Which is a little heavy-handed, admittedly, but does only one scan through the file.

You can also take a look at this, for more answers: https://stackoverflow.com/questions/13380607/how-to-use-sed-to-remove-last-n-lines-of-a-file

Here's a one-liner adapted from one of my favorites there:

count=10
sed -n -e ':a' -e "1,$count"'!{P;N;D;};N;ba'

I had fun deciphering that one, and I hope you do, too (: It does buffer count lines as it scans, but otherwise is pretty efficient — it's a simple sliding window.

Note: the ! is quoted using single-quotes to avoid it being interpreted by Bash (if that's your shell). Plain double-quotes around the whole thing works for sh, possibly others.

jwd
  • 3,735
4

I'm curious why you think head is not an option:

~$ man head
...
-n, --lines=[-]K
        print the first K lines instead of the first 10; 
        with the leading `-', print all but the last K lines of each file

This seems to fit your purpose, using, for example:

head -n -20 yourfile.txt
slhck
  • 235,242
3

Another way to do it if tail -n won't take negative arguments is

tac file.txt | tail -n +6 | tac

This would remove the last 5 lines

atw31337
  • 31
  • 1