使用sed删除大型文件的有效就地标头?


24

以下命令可能需要几分钟的时间,具体取决于文件大小。还有其他更有效的方法吗?

sed -i 1d large_file 

Answers:


34

请尝试ed

ed <<< $'1d\nwq' large_file

如果“大”表示大约1000万行或更多,则更好地使用tail。无法进行就地编辑,但是其性能使其无法原谅:

tail -n +2 large_file > large_file.new

编辑以显示一些时差:

awkJaypal编写的代码被添加为在同一台计算机(CPU 2.2GHz)上具有执行时间。)

bash-4.2$ seq 1000000 > bigfile.txt # further file creations skipped

bash-4.2$ time sed -i 1d bigfile.txt
time 0m4.318s

bash-4.2$ time ed -s <<< $'1d\nwq' bigfile.txt
time 0m0.533s

bash-4.2$ time perl -pi -e 'undef$_ if$.==1' bigfile.txt
time 0m0.626s

bash-4.2$ time { tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt; }
time 0m0.034s

bash-4.2$ time { awk 'NR>1 {print}' bigfile.txt > newfile.txt && mv -f newfile.txt bigfile.txt; }
time 0m0.328s

在情况下tail,我宁愿算时间做除去第一行替换bigfile.txtbigfile.new
rozcietrzewiacz

@rozcietrzewiacz,您的观点是正确的。谢谢。更新。
manatwork 2011年

这真是太酷了!我也这样做了awk,得到了以下结果[jaypal:~/Temp] seq 1000000 > bigfile.txt [jaypal:~/Temp] time awk 'NR>1 {print}' bigfile.txt >newfile.txt real 0m0.649s user 0m0.601s sys 0m0.033s
jaypal singh 2011

1
@Jaypal,我将您的代码添加到了替代列表中。在我的机器上,它甚至更快。奇怪,我期望awk的表现会接近sed。(请注意:永远不要期望–可以代替测试。)
manatwork

对于我而言,这是最好的解决方案:tail -n +2 bigfile.txt > bigfile.new && mv -f bigfile.new bigfile.txt;我正在使用带锁的单个文件来跟踪多个进程使用的单个任务列表。我从最初的海报使用的开始sed -i 1d large_file 。这导致文件锁定1-2秒。该tail/mv组合几乎立即完成。谢谢!
克里斯·亚当斯

6

无法有效地从文件开头删除内容。从一开始就删除数据需要重写整个文件。

但是,从文件末尾截断可能会非常快(操作系统仅需调整文件大小信息,可能会清除当前未使用的块)。当您尝试从文件头删除时,通常这是不可能的。

如果您完全删除了整个块/范围,从理论上讲它可能是“快速”的,但是没有系统调用,因此您必须依靠特定于文件系统的语义(如果存在)。(我想,或者在第一个块/范围内有某种形式的偏移量来标记文件的真实开始。也从未听说过。)


如果文件很大,则I / O开销可能(可能会)大于处理行尾所需的CPU开销。

你是对的。但是,工具访问文件内容的方式可能有所不同。最好不要在不需要时逐行处理,或者至少在不需要时不逐行读取。
manatwork 2011年

2
我很惊讶您的结果之间的差异如此之大,并可以在此处以该文件大小复制它。好处似乎随着文件大小的增加而减少(对seq 10M尝试,sed为15s,ed为5s)。无论如何,都应提供好提示(+1)。

从3.15版开始,Linux现在具有一个API,可以在某种程度上基于文件系统折叠文件的一部分,但是至少对于ext4,这只能在完整块(通常为4k)上完成。
斯特凡Chazelas

即使编辑需要重写整个文件,使用命令行工具进行有效编辑有时也很方便。就我而言,这对我不得不删除大于系统总RAM的文件的第一行很有帮助。
杰森

3

最有效的方法,不要这样做!如果这样做,无论如何,您将需要磁盘上“大”空间的两倍,并且浪费IO。

如果卡住要读取的大文件而没有第一行,请等待直到需要读取它才能删除第一行。如果需要将文件从stdin发送到程序,请使用tail执行此操作:

tail -n +2 | your_program

当您需要读取文件时,可以趁机删除第一行,但前提是磁盘上有所需的空间:

tail -n +2 | tee large_file2 | your_program

如果您无法从stdin中读取内容,请使用fifo:

mkfifo large_file_wo_1st_line
tail -n +2 large_file > large_file_wo_1st_line&
your_program -i large_file_wo_1st_line

如果您正在使用bash,则更好,请利用进程替换:

your_program -i <(tail -n +2 large_file)

如果您需要在文件中进行搜索,那么我没有比不着手使用文件更好的解决方案。如果此文件是由stdout生成的:

large_file_generator | tail -n +2 > large_file

否则,总是有fifo或流程替换解决方案:

mkfifo large_file_with_1st_file
large_file_generator -o large_file_with_1st_file&
tail -n +2 large_file_with_1st_file > large_file_wo_1st_file

large_file_generator -o >(tail -n 2+ > large_file_wo_1st_file)

1

您可以在Ex模式下使用Vim:

ex -sc '1d|x' large_file
  1. 1 选择第一行

  2. d 删除

  3. x 保存并关闭


0

这只是理论上的,但是...

一个自定义文件系统(使用FUSE或类似机制实现)可以公开一个目录,该目录的内容与其他位置已经存在的目录完全相同,但是文件可以根据需要被截断。文件系统将转换所有文件偏移量。这样,您就不必进行费时的文件重写。

但是考虑到这个想法是非常重要的,除非您拥有数十兆字节的此类文件,否则实施这样的文件系统将太昂贵/费时,不切实际。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.