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

Win*_*nix 14 scripts tail sed

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

sed '1d' $file >> headerless.txt
Run Code Online (Sandbox Code Playgroud)

** - - - - - - - - 或者 - - - - - - - - **

tail -n +2 $file >> headerless.txt
Run Code Online (Sandbox Code Playgroud)

我个人认为该tail选项在外观上更令人愉悦且更具可读性,但可能是因为我受到了sed-challenged。

哪种方法最快?

Byt*_*der 28

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

TL; 博士

  • sed 非常强大且用途广泛,但这就是它变慢的原因,尤其是对于包含多行的大文件。

  • tail 只做一件简单的事情,但它做得很好而且很快,即使对于有很多行的大文件也是如此。

对于中小型文件,sed并且tail执行速度相似(或慢,取决于您的期望)。然而,对于较大的输入文件(多个 MB),性能差异显着增加(数百 MB 范围内的文件的数量级),tail明显优于sed.

实验

一般准备:

我们要分析的命令是:

sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null
Run Code Online (Sandbox Code Playgroud)

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

让我们设置一个 RAM 磁盘来消除作为潜在瓶颈的磁盘 I/O。我个人有一个tmpfs安装在,/tmp所以我只是把我的放在testfile那里进行这个实验。

然后我曾经$numoflines使用这个命令创建了一个随机测试文件,其中包含指定数量的具有随机行长度和随机数据的行(请注意,它绝对不是最佳的,大约 >2M 行它变得非常慢,但谁在乎,它不是我们正在分析的东西):

cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile
Run Code Online (Sandbox Code Playgroud)

哦,顺便说一句。我的测试笔记本电脑在 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
Run Code Online (Sandbox Code Playgroud)

使用我们巨大的执行定时运行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
Run Code Online (Sandbox Code Playgroud)

对于大文件,我们已经看到一个非常清晰的结果,tailsed. 但只是为了好玩,并确保没有随机的副作用产生很大的不同,让我们做 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
Run Code Online (Sandbox Code Playgroud)

结论保持不变,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
Run Code Online (Sandbox Code Playgroud)

使用我们的小工具执行定时运行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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,时间非常相似,没有太多需要解释或想知道的。对于小文件,这两种工具同样适用。


Dig*_*uma 5

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

{ read ; cat > headerless.txt; } < $file
Run Code Online (Sandbox Code Playgroud)

$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
$
Run Code Online (Sandbox Code Playgroud)

tail 解决方案:

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

real    0m1.469s
user    0m0.052s
sys 0m0.784s
$ 
Run Code Online (Sandbox Code Playgroud)

cat/支撑解决方案:

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

real    0m1.877s
user    0m0.000s
sys 0m0.736s
$ 
Run Code Online (Sandbox Code Playgroud)

不过,我现在只有一个 Ubuntu 虚拟机,虽然它们都在同一个范围内,但两者的时间都存在显着差异。