在文件中打印单行的最快方法

时间:2013-03-26 08:45:20

标签: bash sed benchmarking head cat

我必须从一个大文件(1500000行)中获取一个特定行,在多个文件的循环中多次,我问自己什么是最好的选择 (在表现方面)。 有很多方法可以做到这一点,我男子气概使用这两个

cat ${file} | head -1

cat ${file} | sed -n '1p'

我找不到这个问题的答案,他们只获取第一行或两个(或两者)中的一个首先打开整个文件,然后获取第1行?

5 个答案:

答案 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:

enter image description here

注意:由于在更快的Linux机器上,时间与原始帖子不同。

答案 1 :(得分:5)

如果您真的只是第一行并阅读数百个文件,那么考虑使用shell内置而不是外部外部命令,使用read这是一个内置于bash和ksh的shell。这消除了使用awksedhead等创建流程的开销。

另一个问题是对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)

避开管道怎么样? sedhead都支持文件名作为参数。这样你就可以避免经过猫。我没有测量它,但是对于较大的文件头应该更快,因为它在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中。