在这个答案中(如何使用 sed 删除文件的第一行?)有两种方法可以删除文件中的第一条记录:
sed '1d' $file >> headerless.txt
** - - - - - - - - 或者 - - - - - - - - **
tail -n +2 $file >> headerless.txt
就我个人而言,我认为这个tail
选项在外观上更令人愉悦且更具可读性,但可能是因为我对 sed 不太熟悉。
哪种方法最快?
答案1
删除文件第一行的sed
vs性能tail
总结
sed
功能强大,用途广泛,但这也导致它运行缓慢,特别是对于包含多行数据的大文件。tail
只做一件简单的事情,但是它做得很好,速度很快,即使对于有很多行的大文件也是如此。
对于中小型文件,sed
和tail
的性能同样快(或慢,取决于您的期望)。但是,对于较大的输入文件(多个 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 上运行 Ubuntu 16.04,64 位。仅用于比较。
对大文件进行计时:
设置一个巨大的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
应该在那里使用。
sed
是的,我知道 Bash 的循环结构很慢,但是我们在这里只进行相对较少的迭代,而且与/运行时间相比,普通循环所花费的时间并不重要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
如您所见,时间非常相似,没有太多需要解释或疑惑的地方。对于小文件,这两种工具同样适用。
答案2
这是另一种选择,仅使用 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,并且发现两者的时间存在很大差异,尽管它们都在同一个范围内。
答案3
在我的系统上尝试,并在每个命令前面加上前缀,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。该文件是从 HDD 读取的。
答案4
最佳答案没有考虑磁盘> /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
不起作用,看起来它不够智能,无法对文件进行增量加载