4

I wanted to tail the last 100 lines of a file to the same file, but the command tail -n 100 file > file doesn't work, I assume because the stdout gets written to the file 'live', before everything was read from the original file.

Is there some way to pipe the output to something, that then keeps it until all 100 lines are there, and then outputs it to the file? Or just another way to shorten the file in this way?

1
  • I wouldn't mark it as a duplicate, as my problem has been wonderfully solved by sponge. Still, the link is good for people (like me) who want to know more about it.
    – MadTux
    Jul 31, 2014 at 12:11

4 Answers 4

11

sponge from moreutils is good for this. It will:

soak up standard input and write to a file

You use it like this:

tail -n 100 file | sponge file

to get exactly the effect you want.

2
  • Much nicer than less :) Also, thanks for introducing me to moreutils. That looks very useful. I think I'll accept this one.
    – MadTux
    Jul 31, 2014 at 9:10
  • Sponge; I love analogy; great name! Apr 29, 2020 at 19:44
2

Of course, the moment I finally ask the question on SE, the answer comes to me. I think less does what I need, so I just write:

tail -n 100 file | less > file

4
  • 1
    It's actually the redirection (>) that truncates the file, rather than the command you run - this won't (or at least shouldn't!) work. The shell opens and empties the destination before it even starts the commands you give. See the (slightly obtuse) description of output redirection in POSIX. Jul 31, 2014 at 9:28
  • 1
    @illuminÉ: That is a neat point. So I should say that this does work very occasionally, but nondeterministically. Jul 31, 2014 at 10:14
  • @MichaelHomer Re this: really just an edge case, just thought of sharing it as it was insightful in context! Cheers!
    – user44370
    Jul 31, 2014 at 10:23
  • OK, thanks for telling me. I think I'll downvote my own answer x) EDIT: I can't. Hmmm.
    – MadTux
    Jul 31, 2014 at 12:09
1

In:

tail -n 100 file > file

The shell forks a process, opens file for writing in it, with truncation (that is making it an empty file) and then executes tail in that process. To open file without truncation, you can use the <> redirection operator instead:

tail -n 100 file 1<> file

The problem though is that there will be no truncation at all. That is, file will be overridden with its last 100 lines, but after those 100 lines, what was originally in the file will still be there. So you'd need to call another command to do the truncating after tail has finished.

{ tail -n 100 file; perl -e 'truncate STDOUT, tell STDOUT'; } 1<> file
0

You can do it this way:

printf '%s\n' "$(tail -n 100 file)" > file
1
  • Unless any lines contain more than one word or are blank. Jul 31, 2014 at 10:02

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