我有一个文件,foo.txt,包含以下行:

a
b
c

我想要一个简单的命令,导致foo.txt的内容为:

a
b

当前回答

对于大文件

我在这里遇到了所有答案的麻烦,因为我正在使用一个巨大的文件(~300Gb),没有一个解决方案缩放。以下是我的解决方案:

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"

或者,作为一行:

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

简言之:找出您想要结束的文件的长度(文件的长度减去其最后一行的长度,使用bc),并将该位置设置为文件的结束位置(通过在其上添加/dev/null的一个字节)。

这是快速的,因为tail从末尾开始读取,dd将就地覆盖文件,而不是复制(并解析)文件的每一行,这是其他解决方案所做的。

注意:这将删除文件中的行!在对自己的文件进行备份或测试之前,先对虚拟文件进行备份或测试!

其他回答

对于大文件

我在这里遇到了所有答案的麻烦,因为我正在使用一个巨大的文件(~300Gb),没有一个解决方案缩放。以下是我的解决方案:

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"

或者,作为一行:

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

简言之:找出您想要结束的文件的长度(文件的长度减去其最后一行的长度,使用bc),并将该位置设置为文件的结束位置(通过在其上添加/dev/null的一个字节)。

这是快速的,因为tail从末尾开始读取,dd将就地覆盖文件,而不是复制(并解析)文件的每一行,这是其他解决方案所做的。

注意:这将删除文件中的行!在对自己的文件进行备份或测试之前,先对虚拟文件进行备份或测试!

awk 'NR>1{print buf}{buf = $0}'

本质上,这段代码表示:

对于第一行之后的每一行,打印缓冲行

对于每一行,重置缓冲区

缓冲区延迟了一行,因此最终打印的是第1行到第n-1行

使用GNU sed:

sed -i '$ d' foo.txt

-i选项在3.95以上的GNU sed版本中不存在,所以你必须使用它作为一个临时文件的过滤器:

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

当然,在这种情况下,你也可以用head -n -1代替sed。

一样:

在Mac OS X(10.7.4)上,与上面的sed -i命令等价的是

sed -i '' -e '$ d' foo.txt

Linux

$表示最后一行,d表示删除:

sed '$d' ~/path/to/your/file/name

操作系统

相当于sed -i:

sed -i '' -e '$ d' ~/path/to/your/file/name

在macOS上,head -n -1不起作用,但你可以使用这个命令:

cat file.txt | tail -r | tail -n +2 | tail -r

Tail -r将其输入中的行顺序颠倒 Tail -n +2输出输入中从第二行开始的所有行