我需要删除 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 压缩文件:
- 在内存中解压缩它以确定要截断它的位置;
- 截断文件以删除要保留的最后一个字符之后的所有数据;
- 覆盖最后几个字节以正确编码最后一个字符;
- 覆盖开头的几个字节以反映新的文件大小。
然而,这不能使用标准工具来完成,它需要一些自定义编程,并且如果中断,将会留下无效文件。
答案2
假设从您的示例中解压缩到流是可以的,但您希望避免解压缩到文件。你应该能够
gzip -cd "$files" | sed -e '$d' | gzip > "$files".tmp
使用sed
转到最后一行并将其删除。
答案3
您可以使用zcat
。
zcat <file> | head -n <lines>
仅解压缩足以流式传输这些n
行。
答案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
重新压缩它。更好的压缩,而且通常更快。