I was trying to remove all the lines of a file except the last line but the following command did not work, although file.txt is not empty.
$cat file.txt |tail -1 > file.txt
$cat file.txt
Why is it so?
I was trying to remove all the lines of a file except the last line but the following command did not work, although file.txt is not empty.
$cat file.txt |tail -1 > file.txt
$cat file.txt
Why is it so?
Before 'cat' gets executed, Bash has already opened 'file.txt' for writing, clearing out its contents.
In general, don't write to files you're reading from in the same statement. This can be worked around by writing to a different file, as above:
or by using a utility like sponge from moreutils: This works because sponge waits until its input stream has ended before opening its output file.This works nicely in a Linux shell:
dd
's "notrunc" option is used to write the filtered contents back, in place, whiledd
is needed again (with a byte count) to actually truncate the file. If the new file size is greater or equal to the old file size, the seconddd
invocation is not necessary.The advantages of this over a file copy method are: 1) no additional disk space necessary, 2) faster performance on large files, and 3) pure shell (other than dd).
tail -1 > file.txt
will overwrite your file, causing cat to read an empty file because the re-write will happen before any of the commands in your pipeline are executed.When you submit your command string to bash, it does the following:
By the time 'cat' starts reading, 'file.txt' has already been truncated by 'tail'.
That's all part of the design of Unix and the shell environment, and goes back all the way to the original Bourne shell. 'Tis a feature, not a bug.
tmp=$(tail -1 file.txt); echo $tmp > file.txt;