在此答案中(如何使用sed删除文件的第一行?),有两种删除文件中第一条记录的方法:
sed '1d' $file >> headerless.txt
** - - - - - - - - 要么 - - - - - - - - **
tail -n +2 $file >> headerless.txt
就我个人而言,我认为该tail
选项在外观上更令人愉悦且更具可读性,但可能是因为我受到了挑战。
哪种方法最快?
tail
缺乏跨平台兼容性的内容。
在此答案中(如何使用sed删除文件的第一行?),有两种删除文件中第一条记录的方法:
sed '1d' $file >> headerless.txt
** - - - - - - - - 要么 - - - - - - - - **
tail -n +2 $file >> headerless.txt
就我个人而言,我认为该tail
选项在外观上更令人愉悦且更具可读性,但可能是因为我受到了挑战。
哪种方法最快?
tail
缺乏跨平台兼容性的内容。
Answers:
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上运行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
如您所见,时间安排非常相似,没有太多需要解释或怀疑的地方。对于小文件,两种工具都同样适用。
awk
也可以做到这一点。我最初的问题基于我首先找到的链接。你所有的努力后请告知我是否应该删除awk
的解决方案候选人,焦点返回到只有原来的项目范围sed
和tail
。
awk 'NR > 1'
很有趣)。
这是另一种选择,仅使用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,虽然两者都在同一范围内,但是两者的时间却有很大差异。
tail
但仍然认为该read
选项非常酷。
在我的系统上进行尝试,并在每个命令前面加上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。从硬盘读取文件。
sed
可能会在此结果中起作用,这就是您对其进行测试的顺序
sed
速度大约是以前的两倍。
没有客观的方法可以说哪个更好,因为在程序执行过程中,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
tail
得更好sed
-请使用。我个人会使用python
或awk
而不是sed
因为它会变得复杂。此外,如果您担心性能,让我们面对现实-您在这里看到的结果以微秒为单位。除非它是您要读取的千兆字节级的巨大文件,否则您不会感到与众不同
awk
答案:)...我的问题是基于另一个AU Q&A(在链接中),在那里他们从未提及awk
。我同意时间差异在小文件上是正常的。我只是想养成一些好习惯。
awk 'NR!=1' input_file.txt
。它给我同样的结果,大约150毫秒,tail
和都相同sed
。但是,我正在使用SSD,所以我要说的是硬盘和CPU至关重要,而不是命令。
sed
耗时超过3分钟,而tail
仅需要20秒左右。实际上那还不是很大,绝对不在GB范围内。
最佳答案未将磁盘考虑在内 > /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
不起作用,则表明它不够智能,无法增量加载文件
sed
是更可移植的:“ +2”表示tail
在使用GNU的Ubuntu上工作正常tail
,但在BSD上不起作用tail
。