删除文件中的第一行…sed或tail哪个更快?


14

在此答案中(如何使用sed删除文件的第一行?),有两种删除文件中第一条记录的方法:

sed '1d' $file >> headerless.txt

** - - - - - - - - 要么 - - - - - - - - **

tail -n +2 $file >> headerless.txt

就我个人而言,我认为该tail选项在外观上更令人愉悦且更具可读性,但可能是因为我受到了挑战。

哪种方法最快?


5
不是答案,而是一个可能的考虑因素,它sed是更可移植的:“ +2”表示tail在使用GNU的Ubuntu上工作正常tail,但在BSD上不起作用tail
约翰N

@JohnN感谢您共享tail缺乏跨平台兼容性的内容。
WinEunuuchs2Unix

3
@John N“ +2”的尾部在运行Sierra的Mac上运行良好,该机器声称使用BSD尾部命令
Nick Sillito 16/12/20

抱歉,您说得对-我已经重新运行它,这次检查了输入。我应该第一次做。也是POSIX。/ slinks,不好意思。
约翰N

2
@JohnN你不是完全错误。过去,UNIX不提供该-n选项,而是使用语法tail +2 $file。请参见freebsd.org/cgi/…。您可能是在考虑这个问题,而不是现代的BSD之一。
hvd

Answers:


28

sedvs. tail删除文件第一行的性能

TL; DR

  • sed 是非常强大且功能强大的工具,但这就是它使速度变慢的原因,特别是对于具有多行的大型文件而言。

  • tail 只会做一件简单的事情,但是那件事情却又好又快,即使对于有很多行的较大文件也是如此。

对于中小型的文件,sedtail正在执行同样快(或慢,取决于你的期望)。但是,对于较大的输入文件(多个MB),性能差异会显着增长(对于数百MB范围内的文件,其数量级是明显的),其性能tail明显优于sed

实验

一般准备:

我们要分析的命令是:

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null

请注意,我/dev/null每次都要对输出进行管道传输,以消除终端输出或文件写入作为性能瓶颈。

让我们设置一个RAM磁盘,以消除可能造成瓶颈的磁盘I / O。我个人tmpfs/tmp所以我只是把我放在testfile那儿进行这个实验。

然后,我一次$numoflines使用此命令创建一个随机测试文件,该文件包含指定数量的具有随机行长和随机数据的行(请注意,这绝对不是最佳选择,对于> 2M的行来说它确实变慢了,但是谁在乎,不是我们正在分析的东西):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile

哦,顺便说一句。我的测试笔记本电脑在Intel i5-6200U CPU上运行64位Ubuntu 16.04。只是为了比较。

定时大文件:

设置一个巨大的testfile

运行上面的命令会numoflines=10000000生成一个随机文件,其中包含10M行,占用了600 MB以上的空间-它非常大,但是让我们开始吧,因为我们可以:

$ wc -l testfile 
10000000 testfile

$ du -h testfile 
611M    testfile

$ head -n 3 testfile 
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO

用我们庞大的计时器进行定时跑步 testfile

现在,让我们先对这两个命令进行一次定时运行,以估算我们正在工作的幅度。

$ time sed '1d' testfile > /dev/null
real    0m2.104s
user    0m1.944s
sys     0m0.156s

$ time tail -n +2 testfile > /dev/null
real    0m0.181s
user    0m0.044s
sys     0m0.132s

对于大文件,我们已经看到了一个非常清晰的结果,tail比快一个数量级sed。但是,只是为了好玩,并确保没有随机的副作用有很大的不同,让我们做100次:

$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real    3m36.756s
user    3m19.756s
sys     0m15.792s

$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real    0m14.573s
user    0m1.876s
sys     0m12.420s

结论保持不变,sed无法删除大文件的第一行,tail应在此处使用。

是的,我知道Bash的循环构造很慢,但是我们在这里只进行了相对较少的迭代,与sed/ 相比,普通循环花费的时间并不重要。tail运行时。

定时小文件:

设置一个小 testfile

现在为完整起见,让我们看一下在kB范围内有一个小的输入文件的更常见情况。让我们使用创建一个随机输入文件numoflines=100,如下所示:

$ wc -l testfile 
100 testfile

$ du -h testfile 
8,0K    testfile

$ head -n 3 testfile 
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly

用我们的小型机进行定时跑步 testfile

正如我们可以预期的那样,此类小文件的计时时间根据经验可能在几毫秒的范围内,让我们立即进行1000次迭代:

$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real    0m7.811s
user    0m0.412s
sys     0m7.020s

$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real    0m7.485s
user    0m0.292s
sys     0m6.020s

如您所见,时间安排非常相似,没有太多需要解释或怀疑的地方。对于小文件,两种工具都同样适用。


+1表示感谢。我根据Serg的评论编辑了原始问题(对不起),awk也可以做到这一点。我最初的问题基于我首先找到的链接。你所有的努力后请告知我是否应该删除awk的解决方案候选人,焦点返回到只有原来的项目范围sedtail
WinEunuuchs2Unix

这是什么系统?在我的Mac(所以是BSD工具)上,在/ usr / share / dict / words上进行的测试为sed提供了0.09s,为tail提供了0.19s(而且awk 'NR > 1'很有趣)。
凯文

5

这是另一种选择,仅使用bash内置函数和cat

{ read ; cat > headerless.txt; } < $file

$file重定向到{ }命令分组。在read简单的读取和丢弃的第一道防线。然后将流的其余部分通过管道传输到cat该流,并将其写入目标文件。

在我的Ubuntu 16.04上,其性能和tail解决方案非常相似。我使用以下命令创建了一个比较大的测试文件seq

$ seq 100000000 > 100M.txt
$ ls -l 100M.txt 
-rw-rw-r-- 1 ubuntu ubuntu 888888898 Dec 20 17:04 100M.txt
$

tail 解:

$ time tail -n +2 100M.txt > headerless.txt

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 

cat/括号解决方案:

$ time { read ; cat > headerless.txt; } < 100M.txt 

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 

我现在只有一台Ubuntu VM,虽然两者都在同一范围内,但是两者的时间却有很大差异。


1
+1的答案谢谢。这是一个非常有趣的解决方案,我喜欢大括号和通过bash的层次结构顺序从右到左阅读。(不确定我的措词是否正确)。如果这样做很容易,是否可以用输入文件的大小和基准测试结果的时间来更新您的答案?
WinEunuuchs2Unix

添加了@ WinEunuuchs2Unix Timings,尽管它们不是很可靠,因为它在VM上。我现在没有裸机Ubuntu安装。
Digital Trauma

无论如何,将VM与VM进行比较时,我认为VM与Bare Metal无关。感谢您的时间证明。我可能会选择,tail但仍然认为该read选项非常酷。
WinEunuuchs2Unix

4

在我的系统上进行尝试,并在每个命令前面加上time以下命令,结果如下:

sed:

real    0m0.129s
user    0m0.012s
sys     0m0.000s

和尾巴:

real    0m0.003s
user    0m0.000s
sys     0m0.000s

这表明,在我的系统上至少运行Ubuntu 16.04的AMD FX 8250上,tail的速度明显更快。测试文件有10,000行,大小为540k。从硬盘读取文件。


+1表示感谢。在AU Chatroom中进行的另一项测试中,一个用户显示,使用具有61 MB文件的RAMDisk,尾巴比sed(21.86秒)快10倍(2.31秒)。我确实编辑了您的答案以应用代码块,但您可能也想使用您使用的文件大小对其进行编辑。
WinEunuuchs2Unix

@Serg绝对的公平,这只是一个传闻的答案,你可能会得到不同的硬件配置不同的结果,不同的测试文件等
尼克Sillito

2
该文件不在缓存中,使用时sed可能会在此结果中起作用,这就是您对其进行测试的顺序
。– Minix

什么样的系统?正如我在这里的另一篇文章中所评论的那样,在我的Mac上sed速度大约是以前的两倍。
凯文

1

没有客观的方法可以说哪个更好,因为在程序执行过程中,sed并且tail不是唯一在系统上运行的东西。许多因素,例如磁盘I / O,网络I / O,较高优先级进程的CPU中断-所有这些因素都会影响程序的运行速度。

两者都是用C编写的,因此这不是语言问题,而是环境问题。例如,我有SSD,而在我的系统上这将花费数微秒的时间,但是对于硬盘上的相同文件,则将花费更多的时间,因为HDD的速度明显降低。因此,硬件也起着重要作用。

在考虑选择哪个命令时,您可能需要记住以下几点:

  • 你的目的是什么?sed是用于转换文本的流编辑器。tail用于输出特定的文本行。如果要处理线条而仅将其打印出来,请使用tail。如果要编辑文本,请使用sed
  • tail语法比的简单得多sed,因此请使用您自己可以阅读的内容以及他人可以阅读的内容。

另一个重要因素是您正在处理的数据量。小文件不会给您带来任何性能差异。处理大文件时,图片变得很有趣。使用2 GB的BIGFILE.txt,我们可以看到它的sed系统调用数量比得多tail,并且运行速度也慢得多。

bash-4.3$ du -sh BIGFILE.txt 
2.0G    BIGFILE.txt
bash-4.3$ strace -c  sed '1d' ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 59.38    0.079781           0    517051           read
 40.62    0.054570           0    517042           write
  0.00    0.000000           0        10         1 open
  0.00    0.000000           0        11           close
  0.00    0.000000           0        10           fstat
  0.00    0.000000           0        19           mmap
  0.00    0.000000           0        12           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         7         7 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         2         2 statfs
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.134351               1034177        11 total
bash-4.3$ strace -c  tail  -n +2 ./BIGFILE.txt  > /dev/null
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 62.30    0.148821           0    517042           write
 37.70    0.090044           0    258525           read
  0.00    0.000000           0         9         3 open
  0.00    0.000000           0         8           close
  0.00    0.000000           0         7           fstat
  0.00    0.000000           0        10           mmap
  0.00    0.000000           0         4           mprotect
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 ioctl
  0.00    0.000000           0         3         3 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.238865                775615         7 total

+1表示感谢。但我不知道这个评论是帮助我决定哪些命令,我应该使用....
WinEunuuchs2Unix

@ WinEunuuchs2Unix好,您问哪个命令更好,所以我正在回答这个问题。由您选择哪个命令。如果您能读tail得更好sed-请使用。我个人会使用pythonawk而不是sed因为它会变得复杂。此外,如果您担心性能,让我们面对现实-您在这里看到的结果以微秒为单位。除非它是您要读取的千兆字节级的巨大文件,否则您不会感到与众不同
Sergiy Kolodyazhnyy

哦,我也希望得到一个awk答案:)...我的问题是基于另一个AU Q&A(在链接中),在那里他们从未提及awk。我同意时间差异在小文件上是正常的。我只是想养成一些好习惯。
WinEunuuchs2Unix

1
@ WinEunuuchs2Unix当然,这是:awk 'NR!=1' input_file.txt 。它给我同样的结果,大约150毫秒,tail和都相同sed。但是,我正在使用SSD,所以我要说的是硬盘和CPU至关重要,而不是命令。
Sergiy Kolodyazhnyy

1
即使只有60 MB的文件(包含100万行),@ Serg也可以运行1000次,sed耗时超过3分钟,而tail仅需要20秒左右。实际上那还不很大,绝对不在GB范围内。
字节指挥官

1

最佳答案未将磁盘考虑在内 > /dev/null

如果文件很大,又不想在磁盘上创建临时副本,请尝试 vim -c

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time sed -i '1d' testfile

real    0m59.053s
user    0m9.625s
sys     0m48.952s

$ cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n 10000000 > testfile
$ time vim -e -s testfile -c ':1d' -c ':wq'

real    0m8.259s
user    0m3.640s
sys     0m3.093s

编辑:如果文件大于可用内存vim -c不起作用,则表明它不够智能,无法增量加载文件


0

其他答案很好地说明了创建缺少第一行的新文件更好的方法。如果您想编辑一个文件而不是创建一个新文件,我敢打赌ed,因为它根本不应该创建一个新文件,所以速度会更快。但是您必须搜索如何删除行,ed因为我只使用过一次。

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.