11

我正在尝试从一个非常非常大的文件中删除前 37 行。我开始尝试 sed 和 awk,但它们似乎需要将数据复制到新文件中。我正在寻找一种“就地删除行”的方法,它不像sed -i不制作任何类型的副本,而只是从现有文件中删除行。

这就是我所做的...

awk 'NR > 37' file.xml > 'f2.xml'
sed -i '1,37d' file.xml

这两个似乎都做了一个完整的副本。是否有任何其他简单的 CLI 可以在不遍历完整文档的情况下快速完成此操作?

4

4 回答 4

13

使用 UNIX 实用程序进行就地编辑没有简单的方法,但这里有一个就地文件修改解决方案,您可以对其进行修改以适合您(由 Robert Bonomi 在https://groups.google.com/forum/#!主题/comp.unix.shell/5PRRZIP0v64):

bytes=$(head -37 "$file" |wc -c)
dd if="$file" bs="$bytes" skip=1 conv=notrunc of="$file"

最终文件的字节数应该$bytes比原始文件小(因为目标是$bytes从头开始删除字节),所以要完成,我们必须删除最后的$bytes字节。我们使用conv=notrunc上面的方法来确保文件不会被完全清空,而不仅仅是被截断(参见下面的例子)。在诸如 Linux 之类的 GNU 系统上,之后可以通过以下方式完成截断:

truncate -s "-$bytes" "$file"

例如从这个 12 行文件中删除前 5 行

$ wc -l file
12 file

$ cat file
When chapman billies leave the street,
And drouthy neibors, neibors, meet;
As market days are wearing late,
And folk begin to tak the gate,
While we sit bousing at the nappy,
An' getting fou and unco happy,
We think na on the lang Scots miles,
The mosses, waters, slaps and stiles,
That lie between us and our hame,
Where sits our sulky, sullen dame,
Gathering her brows like gathering storm,
Nursing her wrath to keep it warm.

首先用于dd从文件开头删除目标 5 行(实际上是“$bytes”字节),并将其余部分从末尾复制到前面,但保留尾随的“$bytes”字节原样:

$ bytes=$(head -5 file |wc -c)

$ dd if=file bs="$bytes" skip=1 conv=notrunc of=file
1+1 records in
1+1 records out
253 bytes copied, 0.0038458 s, 65.8 kB/s

$ wc -l file
12 file

$ cat file
An' getting fou and unco happy,
We think na on the lang Scots miles,
The mosses, waters, slaps and stiles,
That lie between us and our hame,
Where sits our sulky, sullen dame,
Gathering her brows like gathering storm,
Nursing her wrath to keep it warm.
s, waters, slaps and stiles,
That lie between us and our hame,
Where sits our sulky, sullen dame,
Gathering her brows like gathering storm,
Nursing her wrath to keep it warm.

然后用于truncate从末尾删除那些剩余的字节:

$ truncate -s "-$bytes" "file"

$ wc -l file
7 file

$ cat file
An' getting fou and unco happy,
We think na on the lang Scots miles,
The mosses, waters, slaps and stiles,
That lie between us and our hame,
Where sits our sulky, sullen dame,
Gathering her brows like gathering storm,
Nursing her wrath to keep it warm.

如果我们没有尝试上述方法dd ... conv=notrunc

$ wc -l file
12 file
$ bytes=$(head -5 file |wc -c)
$ dd if=file bs="$bytes" skip=1 of=file
dd: file: cannot skip to specified offset
0+0 records in
0+0 records out
0 bytes copied, 0.0042254 s, 0.0 kB/s
$ wc -l file
0 file

有关其他建议和信息,请参阅我引用的 google groups 线程。

于 2013-06-26T21:57:07.863 回答
6

Unix 文件语义不允许截断文件的前面部分。

所有解决方案都将基于:

  1. 将文件读入内存,然后将其写回(ed, ex, 其他编辑器)。如果您的文件小于 1GB 或者您有足够的 RAM,这应该没问题。
  2. 编写第二份副本并可选择替换原件 ( sed -i, awk/ tail > foo)。只要您有足够的可用磁盘空间进行副本,这很好,并且不介意等待。

如果文件太大,其中任何一个都无法为您工作,您可以根据正在读取文件的内容来解决它。

也许您的读者会跳过评论或空白行?如果是这样,您可以制作一条读者忽略的消息,确保它与文件中的前 37 行具有相同的字节数,并用dd if=yourdata of=file conv=notrunc.

于 2013-06-26T21:15:26.040 回答
5

是标准编辑器:

ed -s file <<< $'1,37d\nwq'
于 2013-06-26T20:57:10.173 回答
2

必须在某个时候创建​​副本——为什么不在读取“修改”文件时创建;流式传输更改的副本而不是存储它?

我在想什么 - 创建一个命名管道“file2”,它是同一个 awk 'NR > 37' file.xml 或其他任何东西的输出;那么读取 file2 的人将看不到前 37 行。

缺点是每次处理文件都会运行awk,所以只有在很少读取的情况下才可行。

于 2013-06-26T21:19:07.823 回答