我必须从一个大文件(1500000行)中获取一个特定行,在多个文件的循环中多次,我问自己什么是最好的选择 (在表现方面)。 有很多方法可以做到这一点,我男子气概使用这两个
cat ${file} | head -1
或
cat ${file} | sed -n '1p'
我找不到这个问题的答案,他们只获取第一行或两个(或两者)中的一个首先打开整个文件,然后获取第1行?
答案 0 :(得分:28)
放弃对cat
的无用使用并执行:
$ sed -n '1{p;q}' file
这将在打印行后退出sed
脚本。
基准测试脚本:
#!/bin/bash
TIMEFORMAT='%3R'
n=25
heading=('head -1 file' 'sed -n 1p file' "sed -n '1{p;q} file" 'read line < file && echo $line')
# files upto a hundred million lines (if your on slow machine decrease!!)
for (( j=1; j<=100,000,000;j=j*10 ))
do
echo "Lines in file: $j"
# create file containing j lines
seq 1 $j > file
# initial read of file
cat file > /dev/null
for comm in {0..3}
do
avg=0
echo
echo ${heading[$comm]}
for (( i=1; i<=$n; i++ ))
do
case $comm in
0)
t=$( { time head -1 file > /dev/null; } 2>&1);;
1)
t=$( { time sed -n 1p file > /dev/null; } 2>&1);;
2)
t=$( { time sed '1{p;q}' file > /dev/null; } 2>&1);;
3)
t=$( { time read line < file && echo $line > /dev/null; } 2>&1);;
esac
avg=$avg+$t
done
echo "scale=3;($avg)/$n" | bc
done
done
只需保存为benchmark.sh
并运行bash benchmark.sh
即可。
<强>结果:强>
head -1 file
.001
sed -n 1p file
.048
sed -n '1{p;q} file
.002
read line < file && echo $line
0
**来自1,000,000行的文件的结果。*
因此sed -n 1p
的时间会随着文件的长度呈线性增长,但其他变化的时间将是恒定的(并且可以忽略不计)因为它们在读完第一个后都会退出line:
注意:由于在更快的Linux机器上,时间与原始帖子不同。
答案 1 :(得分:5)
如果您真的只是第一行并阅读数百个文件,那么考虑使用shell内置而不是外部外部命令,使用read
这是一个内置于bash和ksh的shell。这消除了使用awk
,sed
,head
等创建流程的开销。
另一个问题是对I / O进行定时性能分析。第一次打开然后读取文件时,文件数据可能没有缓存在内存中。但是,如果再次对同一文件尝试第二个命令,则数据和inode都已缓存,因此定时结果可能更快,几乎与您使用的命令无关。此外,inode几乎可以永远保持缓存。例如,它们在Solaris上运行。或者无论如何,好几天。
例如,linux缓存所有内容和厨房接收器,这是一个很好的性能属性。但如果您不了解这个问题,它会使基准测试成为问题。
所有这些缓存效果&#34;干扰&#34;是操作系统和硬件相关的。
所以 - 选择一个文件,用命令读取。现在它被缓存了。运行相同的测试命令几十次,这是对命令和子进程创建的效果进行采样,而不是I / O硬件。
在读取文件一次后,这是获取同一文件的第一行的10次迭代的sed vs读取:
sed:sed '1{p;q}' uopgenl20121216.lis
real 0m0.917s
user 0m0.258s
sys 0m0.492s
阅读:read foo < uopgenl20121216.lis ; export foo; echo "$foo"
real 0m0.017s
user 0m0.000s
sys 0m0.015s
这显然是做作的,但确实显示了内置性能与使用命令之间的区别。
答案 2 :(得分:4)
避开管道怎么样?
sed
和head
都支持文件名作为参数。这样你就可以避免经过猫。我没有测量它,但是对于较大的文件头应该更快,因为它在N行之后停止计算(而sed遍历所有这些,即使它不打印它们 - 除非你指定q
如上所述的uit选项。)
示例:
sed -n '1{p;q}' /path/to/file
head -n 1 /path/to/file
同样,我没有测试效率。
答案 3 :(得分:2)
如果你想从大文件中只打印1行(比如第20行),你也可以这样做:
head -20 filename | tail -1
我做了一个&#34;基本&#34;使用bash进行测试,它似乎比上面的sed -n '1{p;q}
解决方案表现更好。
测试需要一个大文件并从中间某处打印一行(在行10000000
处),重复100次,每次选择下一行。因此,它会选择行10000000,10000001,10000002, ...
,依此类推,直到10000099
$wc -l english
36374448 english
$time for i in {0..99}; do j=$((i+10000000)); sed -n $j'{p;q}' english >/dev/null; done;
real 1m27.207s
user 1m20.712s
sys 0m6.284s
VS
$time for i in {0..99}; do j=$((i+10000000)); head -$j english | tail -1 >/dev/null; done;
real 1m3.796s
user 0m59.356s
sys 0m32.376s
用于打印多个文件中的一行
$wc -l english*
36374448 english
17797377 english.1024MB
3461885 english.200MB
57633710 total
$time for i in english*; do sed -n '10000000{p;q}' $i >/dev/null; done;
real 0m2.059s
user 0m1.904s
sys 0m0.144s
$time for i in english*; do head -10000000 $i | tail -1 >/dev/null; done;
real 0m1.535s
user 0m1.420s
sys 0m0.788s
答案 4 :(得分:0)
我已经进行了广泛的测试,发现如果想要文件的每一行,
while IFS=$'\n' read LINE; do
echo "$LINE"
done < your_input.txt
比其他任何(基于Bash的)方法快了很多。所有其他方法(例如sed
)每次都读取文件,至少直到匹配行为止。如果文件长4行,您将得到:1 -> 1,2 -> 1,2,3 -> 1,2,3,4
= 10
读取,而while循环仅维护位置光标(基于IFS
),因此只能执行{{1} }总共读取。
在约有1.5万行的文件中,差异是惊人的:约25-28秒(基于4
,每次提取特定行)与约0-1秒(基于sed
) ,一次读取文件)
上面的示例还显示了如何以更好的方式将while...read
设置为换行符(感谢彼得在下面的评论中),这有望解决使用IFS
时遇到的其他一些问题有时会在Bash中。