42

Is there a way in Linux to ask for the Head or Tail but with an additional offset of records to ignore.

For example if the file example.lst contains the following:

row01
row02
row03
row04
row05

And I use head -n3 example.lst I can get rows 1 - 3 but what if I want it to skip the first row and get rows 2 - 4?

I ask because some commands have a header which may not be desirable within the search results. For example du -h ~ --max-depth 1 | sort -rh will return the directory size of all folders within the home directory sorted in descending order but will append the current directory to the top of the result set (i.e. ~).

The Head and Tail man pages don't seem to have any offset parameter so maybe there is some kind of range command where the required lines can be specified: e.g. range 2-10 or something?

hash-bang
  • 492
  • 1
  • 4
  • 9

4 Answers4

64

From man tail:

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

You can therefore use ... | tail -n +2 | head -n 3 to get 3 lines starting from line 2.

Non-head/tail methods include sed -n "2,4p" and awk "NR >= 2 && NR <= 4".

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • 6
    +1 You could also do `awk 'NR>4{exit}NR>=2'` to exit after the 4th row. – John B Aug 14 '14 at 01:55
  • 1
    wtf this answer makes no sense - how can we use `head` or `tail` to get a range. I would have expect an example in this answer like `tail -n 5 -l 5` first option being where to start second being how many –  Jun 09 '19 at 02:04
  • 2
    @MrCholo The answer explains how, but if it's not clear, you can give me a line range and I'll give you the head+tail combo to extract it – that other guy Jun 09 '19 at 04:07
  • well I know that if you do this: `tail -n 10 | head -n 5`, it will read the "second-to-last set of 5", however, imagine a bigger number. `tail -n 1000 | head -n 5`, now it has to read 1000 things which is slower. I am looking for a single command that can do this and only read in 5 lines. something like `range --start '-1000' --end '-1005'`, notice the negative numbers –  Jun 09 '19 at 04:09
  • 1
    Files are not line addressable. If you want to access lines by line number without reading through the file from start or end, you will have to index the file in advance, or make all lines a known length. – that other guy Jun 09 '19 at 04:16
6

To get the rows between 2 and 4 (both inclusive), you can use:

head -n4 example.lst | tail -n+2

or

head -n4 example.lst | tail -n3
Farahmand
  • 2,731
  • 1
  • 24
  • 27
2

It took make a lot of time to end-up with this solution which, seems to be the only one that covered all usecases (so far):

command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
          '{
               if (NR <= offset) print;
               else {
                   a[NR] = $0;
                   delete a[NR-offset];
                   printf "." > "/dev/stderr"
                   }
           }
           END {
             print "" > "/dev/stderr";
             for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
             { print a[i]}
           }'

Feature list:

  • live output for head (obviously that for tail is not possible)
  • no use of external files
  • progressbar on stderr, one dot for each line after the MAX_LINES, very useful for long running tasks.
  • avoids possible incorrect logging order due to buffering (stdbuf)
sorin
  • 161,544
  • 178
  • 535
  • 806
2

sed -n 2,4p somefile.txt

#fill

Agnel Kurian
  • 57,975
  • 43
  • 146
  • 217