删除gz文件的最后一行

删除gz文件的最后一行

我需要删除 gz 文件的最后一行而不解压缩。该文件有 500 行。

我怎样才能做到这一点?

我努力了:

 gzip -dc "$files" | tail -500 | gzip -c > "$files".tmp

但它不起作用。

答案1

如果不解压缩压缩文件,则无法对其进行修改。

至少,要删除第 499 行之后的所有文本,您必须解压缩前 499 行以找到第 499 行的结束位置。如果无论有多少行都想删除最后一行,则需要解压缩整个文件以确定最后一行的开始位置。

因为文件是压缩的,所以没有快捷方式。字符的编码取决于之前的所有字符——gzip压缩的基本原理是对于之前遇到过的字符序列使用较短的位序列,对于还没有遇到过的字符序列使用稍长的位序列,因此当字符序列重复时产生较小的文件。如果不检查所有前面的字符,就无法确定特定字符是换行符。

您的尝试是解压缩文件,处理解压缩的流,然后重新压缩到另一个文件,这是正确的。您只需要正确的命令来截断文件:tail -500保留最后 500 行,这不是您想要的。用于head -n 499保留前 499 行,或head -n -1删除最后一行。并非所有系统都支持否定论证head;如果你的没有,你可以用它sed '$d'来代替。

gunzip <"$file" | head -n -1 | gzip >"$file".tmp
mv -- "$file".tmp "$file"

请注意,您不能直接写入文件:将在仍在读取文件gunzip <"$file" | … | gzip >"$file"时开始覆盖文件。gunzip管道中的命令是并行执行的。虽然可以避免创建临时文件,但这不是一个好主意,因为如果命令中断,任何这样做的方法都会导致文件被截断,所以我不会讨论如何做到这一点。

理论上,可以通过以下方式截断 gzip 压缩文件:

  1. 在内存中解压缩它以确定要截断它的位置;
  2. 截断文件以删除要保留的最后一个字符之后的所有数据;
  3. 覆盖最后几个字节以正确编码最后一个字符;
  4. 覆盖开头的几个字节以反映新的文件大小。

然而,这不能使用标准工具来完成,它需要一些自定义编程,并且如果中断,将会留下无效文件。

答案2

假设从您的示例中解压缩到流是可以的,但您希望避免解压缩到文件。你应该能够

gzip -cd "$files" | sed -e '$d' | gzip > "$files".tmp

使用sed转到最后一行并将其删除。

答案3

您可以使用zcat

zcat <file> | head -n <lines>

仅解压缩足以流式传输这些n行。

进一步阅读:http://www.thegeekstuff.com/2009/05/zcat-zless-zgrep-zdiff-zcmp-zmore-gzip-file-operations-on-the-compressed-files/

答案4

基于 @Eric Renouf 的答案(抱歉,对于评论来说太长了),要将原始时间戳和文件名元数据保留在文件中,请将其包装为:

gzip -cd "$file" | sed -e '$d' > "$file.tmp"
touch -r "$file" "$file.tmp"
# optionally keep the old file
# mv "$files" "$file.old"
mv "$file.tmp" "$file"
gzip "$file"

或者,由于那里有一个未压缩的文件,请使用 axz而不是gzip重新压缩它。更好的压缩,而且通常更快。

相关内容