Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove the last line from a file in Bash

I have a file, foo.txt, containing the following lines:

a
b
c

I want a simple command that results in the contents of foo.txt being:

a
b
like image 582
Fragsworth Avatar asked Feb 03 '11 01:02

Fragsworth


4 Answers

Using GNU sed:

sed -i '$ d' foo.txt

The -i option does not exist in GNU sed versions older than 3.95, so you have to use it as a filter with a temporary file:

cp foo.txt foo.txt.tmp
sed '$ d' foo.txt.tmp > foo.txt
rm -f foo.txt.tmp

Of course, in that case you could also use head -n -1 instead of sed.

MacOS:

On Mac OS X (as of 10.7.4), the equivalent of the sed -i command above is

sed -i '' -e '$ d' foo.txt
like image 152
thkala Avatar answered Oct 23 '22 22:10

thkala


This is by far the fastest and simplest solution, especially on big files:

head -n -1 foo.txt > temp.txt ; mv temp.txt foo.txt

if You want to delete the top line use this:

tail -n +2 foo.txt

which means output lines starting at line 2.

Do not use sed for deleting lines from the top or bottom of a file -- it's very very slow if the file is large.

like image 38
John Avatar answered Oct 23 '22 20:10

John


For large files

I had trouble with all the answers here because I was working with a HUGE file (~300Gb) and none of the solutions scaled. Here's my solution:

filename="example.txt"

file_size="$(stat --format=%s "$filename")"
trim_count="$(tail -n1 "$filename" | wc -c)"
end_position="$(echo "$file_size - $trim_count" | bc)"

dd if=/dev/null of="$filename" bs=1 seek="$end_position"

Or alternatively, as a one liner:

dd if=/dev/null of=<filename> bs=1 seek=$(echo $(stat --format=%s <filename> ) - $( tail -n1 <filename> | wc -c) | bc )

In words: Find out the length of the file you want to end up with (length of file minus length of length of its last line, using bc), and set that position to be the end of the file (by dding one byte of /dev/null onto it).

This is fast because tail starts reading from the end, and dd will overwrite the file in place rather than copy (and parse) every line of the file, which is what the other solutions do.

NOTE: This removes the line from the file in place! Make a backup or test on a dummy file before trying it out on your own file!

like image 148
Yossi Farjoun Avatar answered Oct 23 '22 22:10

Yossi Farjoun


To remove the last line from a file without reading the whole file or rewriting anything, you can use

tail -n 1 "$file" | wc -c | xargs -I {} truncate "$file" -s -{}

To remove the last line and also print it on stdout ("pop" it), you can combine that command with tee:

tail -n 1 "$file" | tee >(wc -c | xargs -I {} truncate "$file" -s -{})

These commands can efficiently process a very large file. This is similar to, and inspired by, Yossi's answer, but it avoids using a few extra functions.

If you're going to use these repeatedly and want error handling and some other features, you can use the poptail command here: https://github.com/donm/evenmoreutils

like image 75
ohspite Avatar answered Oct 23 '22 21:10

ohspite