How to obtain inverse behavior for `tail` and `head`?

55,029

Solution 1

You can use this to strip the first two lines:

tail -n +3 foo.txt

and this to strip the last two lines:

head -n -2 foo.txt

(assuming the file ends with \n for the latter)


Just like for the standard usage of tail and head these operations are not destructive. Use >out.txt if you want to redirect the output to some new file:

tail -n +3 foo.txt >out.txt

In the case out.txt already exists, it will overwrite this file. Use >>out.txt instead of >out.txt if you'd rather have the output appended to out.txt.

Solution 2

If you want all but the first N-1 lines, call tail with the number of lines +N. (The number is the number of the first line you want to retain, starting at 1, i.e. +1 means start at the top, +2 means skip one line and so on).

tail -n +3 foo.txt >>other-document

There's no easy, portable way to skip the last N lines. GNU head allows head -n +N as a counterpart of tail -n +N. Otherwise, if you have tac (e.g. GNU or Busybox), you can combine it with tail:

tac | tail -n +3 | tac

Portably, you can use an awk filter (untested):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

If you want to remove the last few lines from a large file, you can determine the byte offset of the piece to truncate then perform the truncation with dd.

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

You can't truncate a file in place at the beginning, though if you need to remove the first few lines of a huge file, you can move the contents around.

Solution 3

To remove the first n lines GNU sed can be used. For example if n = 2

sed -n '1,2!p' input-file

The ! mean "exclude this interval". As you can imagine, more complicated result can be obtained, for example

sed -n '3,5p;7p'

that will show line 3,4,5,7. More power come from use of regular expressions instead of addresses.

The limitation is that the lines numbers must be known in advance.

Solution 4

From the tail man page (GNU tail, that is):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Thus, the following should append all but the first 2 lines of somefile.txt to anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt

Solution 5

{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

If <infile is a regular, lseek()-able file, then yes, by all means, feel free. The above is a fully POSIXly supported construct.

Share:
55,029

Related videos on Youtube

chrisjlee
Author by

chrisjlee

@chrisjlee

Updated on September 18, 2022

Comments

  • chrisjlee
    chrisjlee almost 2 years

    Is there a way to head/tail a document and get the reverse output; because you don't know how many lines there are in a document?

    I.e. I just want to get everything but the first 2 lines of foo.txt to append to another document.

  • Stéphane Gimenez
    Stéphane Gimenez almost 13 years
    @fred: Strange indeed… (same with 8.12 here).
  • chrisjlee
    chrisjlee almost 13 years
    Is this operation destructive? As i want it to copy the inverse of the first two lines of the document to another?
  • Stéphane Gimenez
    Stéphane Gimenez almost 13 years
    @Chris: No, they just print the result on their "standard output", which is usually connected to the terminal. I've added some details on how to redirect the output to some files.
  • l0b0
    l0b0 about 11 years
    head -n -2 is not POSIX compatible.
  • SirMarv.....TheBinaryTechie
    SirMarv.....TheBinaryTechie almost 10 years
    head -n -2 foo.txt says head: illegal line count -- -2
  • Stéphane Chazelas
    Stéphane Chazelas about 9 years
    On some systems (like modern Linux), you can truncate (collapse) a file in place at the beginning, but usually only by an amount that is multiple of the FS block size (so not really useful in this case).
  • Wildcard
    Wildcard over 7 years
    Why not just sed 1,2d? Simpler is usually better. Also, nothing in your examples is specific to GNU Sed; your commands all use standard POSIX features of Sed.