Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

It can be done with other tools, but I am interested to know how can I delete all but the last X lines of the file with sed.

share|improve this question
4  
The hard way. Stick with the other tools. – Ignacio Vazquez-Abrams Sep 7 '12 at 2:22

Basically you are emulating tail. X = 20 in this example. The following example will delete all but the last 20 lines:

sed -e :a -e '$q;N;21,$D;ba' filename

Explanation:

  • The -e :a creates a label called a
  • The next -e:
    • $q - quits and prints the pattern space if it is the last line
    • N - next line
    • 21,$D - executes the "D" command if the line# is >= 21 (21,$ = 21st line to $ which is the end of the file)
    • ba - branches to label 'a' which is the beginning of the script.
share|improve this answer
    
It works as tail, but I was not able to make it work in place (as the in the original question) – Matteo Sep 7 '12 at 6:48
2  
+1... BTW. It works with -i if you condense the two -e expressions into one. You can also use it by passing bash variable $1 into it... sed -i ':a;$q;N;'$(($1+1))',$D;ba' filename ... but asking for 0 lines, will return 1 line. – Peter.O Sep 7 '12 at 7:42
    
@Peter.O That just blew my mind. Could you please explain this bit: '$(($1+1))' ?? – tjt263 Jun 5 at 22:02

sed is quite complex when it comes to task like this one. tail, grep or awk would make this a lot easier and should be used instead. That being said, it is possible.

The following solution is adapted from sed and Multi-Line Search and Replace.

sed -ni '
    # if the first line copy the pattern to the hold buffer
    1h
    # if not the first line then append the pattern to the hold buffer
    1!H
    # if the last line then ...
    ${
            # copy from the hold to the pattern buffer
            g
            # delete current line if it is followed by at least X more lines
            # replace X-1 with the proper value
            s/.*\n\(\(.*\n\)\{X-1\}\)/\1/
            # print
            p
    }
' filename

Without the comments, it makes a nifty one-liner. If you want to eliminate, e.g., everything but the last ten lines, use this:

sed -ni '1h;1!H;${;g;s/.*\n\(\(.*\n\)\{9\}\)/\1/;p;}' filename
share|improve this answer
    
On non-GNU seds you need -e after -i. – Matteo Sep 7 '12 at 6:46
    
Just like phiz's answer, this does not handle a request for 0 last lines... However, that is one of the reasons for the recommendation to use another tool. – Peter.O Sep 7 '12 at 7:49

Based on the script in section 4.13 of the sed.info manual you could do something like this:

n=10

(( n > 1 )) && script='1h; 2,'$n'{H;g;}; $q; 1,'$((n-1))'d; N; D'
(( n > 1 )) || script='$!d'

sed -i "$script" infile
share|improve this answer

tac|sed|tac>&&(mv||cat>)

Both of the following command snippets will effectively delete all but the very last 5 lines of ~/file1. If you want to retain the last 10 lines, you can substitute: |sed '1,5!d;' with |sed '1,10!d;', and so on, as you see fit.

  1. tac ~/"file1" |sed '1,5!d;' |tac >"/tmp/file2" &&mv  "/tmp/file2"  ~/"file1"
  2. tac ~/"file1" |sed '1,5!d;' |tac >"/tmp/file2" &&cat "/tmp/file2" >~/"file1"
share|improve this answer

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .