根据我的测试,在性能和可读性方面,我的建议是:
tail -n+N | head -1
N 是您想要的行号。例如,tail -n+7 input.txt | head -1 将打印文件的第 7 行。
tail -n+N 将从N 行开始打印所有内容,head -1 将在一行后停止。
替代的head -N | tail -1 可能更具可读性。例如,这将打印第 7 行:
head -7 input.txt | tail -1
在性能方面,较小尺寸的差异不大,但当文件变大时,它的性能将优于tail | head(上图)。
投票率最高的sed 'NUMq;d' 很有趣,但我认为与头/尾解决方案相比,开箱即用的人会更少,而且它也比尾/头慢。
在我的测试中,两个尾部/头部版本的性能始终优于 sed 'NUMq;d'。这与发布的其他基准一致。很难找到反面/正面非常糟糕的情况。这也不足为奇,因为这些是您期望在现代 Unix 系统中进行大量优化的操作。
为了了解性能差异,以下是我为大文件 (9.3G) 获得的数字:
-
tail -n+N | head -1:3.7 秒
-
head -N | tail -1:4.6 秒
-
sed Nq;d:18.8 秒
结果可能会有所不同,但head | tail 和tail | head 的性能通常与较小的输入相当,而sed 的速度总是慢很多(大约 5 倍左右)。
要重现我的基准,您可以尝试以下操作,但要注意它会在当前工作目录中创建一个 9.3G 文件:
#!/bin/bash
readonly file=tmp-input.txt
readonly size=1000000000
readonly pos=500000000
readonly retries=3
seq 1 $size > $file
echo "*** head -N | tail -1 ***"
for i in $(seq 1 $retries) ; do
time head "-$pos" $file | tail -1
done
echo "-------------------------"
echo
echo "*** tail -n+N | head -1 ***"
echo
seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
time tail -n+$pos $file | head -1
done
echo "-------------------------"
echo
echo "*** sed Nq;d ***"
echo
seq 1 $size > $file
ls -alhg $file
for i in $(seq 1 $retries) ; do
time sed $pos'q;d' $file
done
/bin/rm $file
这是在我的机器上运行的输出(ThinkPad X1 Carbon,带有 SSD 和 16G 内存)。我假设在最终运行中,所有内容都来自缓存,而不是磁盘:
*** head -N | tail -1 ***
500000000
real 0m9,800s
user 0m7,328s
sys 0m4,081s
500000000
real 0m4,231s
user 0m5,415s
sys 0m2,789s
500000000
real 0m4,636s
user 0m5,935s
sys 0m2,684s
-------------------------
*** tail -n+N | head -1 ***
-rw-r--r-- 1 phil 9,3G Jan 19 19:49 tmp-input.txt
500000000
real 0m6,452s
user 0m3,367s
sys 0m1,498s
500000000
real 0m3,890s
user 0m2,921s
sys 0m0,952s
500000000
real 0m3,763s
user 0m3,004s
sys 0m0,760s
-------------------------
*** sed Nq;d ***
-rw-r--r-- 1 phil 9,3G Jan 19 19:50 tmp-input.txt
500000000
real 0m23,675s
user 0m21,557s
sys 0m1,523s
500000000
real 0m20,328s
user 0m18,971s
sys 0m1,308s
500000000
real 0m19,835s
user 0m18,830s
sys 0m1,004s