有没有一种“规范”的方法?我一直在使用head-n|tail-1来完成这个任务,但我一直在想是否有一个Bash工具专门从文件中提取一行(或一系列行)。

我所说的“规范”是指一个主要功能就是这样做的程序。


当前回答

我将上面的一些答案放入了一个简短的bash脚本中,您可以将其放入名为get.sh的文件中,并链接到/usr/local/bin/get(或您喜欢的任何其他名称)。

#!/bin/bash
if [ "${1}" == "" ]; then
    echo "error: blank line number";
    exit 1
fi
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
    echo "error: line number arg not a number";
    exit 1
fi
if [ "${2}" == "" ]; then
    echo "error: blank file name";
    exit 1
fi
sed "${1}q;d" $2;
exit 0

确保它可通过

$ chmod +x get

将其链接以使其在PATH上可用

$ ln -s get.sh /usr/local/bin/get

其他回答

哇,所有的可能性!

试试看:

sed -n "${lineNum}p" $file

或者根据您的Awk版本选择其中之一:

awk  -vlineNum=$lineNum 'NR == lineNum {print $0}' $file
awk -v lineNum=4 '{if (NR == lineNum) {print $0}}' $file
awk '{if (NR == lineNum) {print $0}}' lineNum=$lineNum $file

(您可能需要尝试nawk或gawk命令)。

是否有一种工具只打印特定的行?不是标准工具之一。然而,sed可能是最接近和最简单的用法。

使用其他人提到的内容,我希望这是我的bash shell中一个快速而出色的函数。

创建文件:~/.functions

添加内容:

getline(){行=$1sed$line'q;2美元}

然后将其添加到~/.bash_profile中:

source~/.函数

现在,当您打开一个新的bash窗口时,您可以这样调用函数:

获取行441 myfile.txt

要使用sed和变量作为行号打印第n行,请执行以下操作:

a=4
sed -e $a'q:d' file

这里的“-e”标志用于将脚本添加到要执行的命令中。

我有一个独特的情况,我可以对本页上提出的解决方案进行基准测试,因此我将此答案作为所提出解决方案的合并,并包含每个解决方案的运行时间。

设置

我有一个3.261千兆字节的ASCII文本数据文件,每行有一个键值对。该文件共包含3339550320行,无法在我尝试过的任何编辑器中打开,包括使用Vim。我需要对这个文件进行子集,以便调查我发现的一些值,这些值仅从第~500000000行开始。

因为文件有很多行:

我只需要提取行的一个子集,就可以对数据进行任何有用的操作。通读每一行,得出我所关心的值,需要很长时间。如果解决方案读取了我关心的行,并继续读取文件的其余部分,那么将浪费时间读取近30亿个不相关的行,所需时间将比需要的时间长6倍。

我最好的方案是只从文件中提取一行,而不读取文件中的任何其他行,但我想不出如何在Bash中实现这一点。

为了我的理智,我不会试图阅读我自己的问题所需要的全部500000000行。相反,我将尝试从3339550320中提取第50000000行(这意味着读取整个文件需要比所需时间长60倍)。

我将使用内置的时间对每个命令进行基准测试。

基线

首先,让我们看看头尾解决方案是如何实现的:

$ time head -50000000 myfile.ascii | tail -1
pgm_icnt = 0

real    1m15.321s

5000万行的基线是00:01:15.321,如果我直冲5亿行,大概需要12.5分钟。

cut

我对这一点半信半疑,但值得一试:

$ time cut -f50000000 -d$'\n' myfile.ascii
pgm_icnt = 0

real    5m12.156s

这只跑了00:05:12.156,比基线慢得多!我不确定它是在停止之前读取整个文件还是仅读取5000万行,但无论如何,这似乎不是解决问题的可行方案。

AWK

我只使用出口运行解决方案,因为我不打算等待完整文件运行:

$ time awk 'NR == 50000000 {print; exit}' myfile.ascii
pgm_icnt = 0

real    1m16.583s

这段代码运行时间为00:01:16.583,仅慢了约1秒,但与基线相比仍没有改善。按照这个速度,如果退出命令被排除,那么读取整个文件可能需要大约76分钟!

Perl

我还运行了现有的Perl解决方案:

$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii
pgm_icnt = 0

real    1m13.146s

该代码在00:01:13.146运行,比基线快了约2秒。如果我用5000万美元来运行它,可能需要大约12分钟。

sed

上面的答案是我的结果:

$ time sed "50000000q;d" myfile.ascii
pgm_icnt = 0

real    1m12.705s

这段代码以00:01:12.705运行,比基线快3秒,比Perl快0.4秒。如果我在整个500000000行上运行它,可能需要大约12分钟。

映射文件

我有bash 3.1,因此无法测试mapfile解决方案。

结论

看起来,在大多数情况下,很难改进头尾解决方案。最好情况下,sed解决方案可提高约3%的效率。

(使用公式%=(运行时/基线-1)*100计算的百分比)

第50000000行

00:01:12.705(-00:00:02.616=-3.47%)秒00:01:13.146(00:00:02.175=-2.89%)perl00:01:15.321(+00:00:00.000=+0.00%)头部|尾部00:01:16.583(+00:00:01.262=+1.68%)awk00:05:12.156(+000:03:56.835=+314.43%)切割

第500000000行

00:12:07.050(-00:00:26.160)秒00:12:11.460(-00:00:21.750)佩尔00:12:33.210(+00:00:00.000)头|尾00:12:45.830(+00:00:12.620)awk00:52:01.560(+00:40:31.650)切割

行3338559320

01:20:54.599(-00:03:05.327)秒01:21:24.045(-00:02:25.227)佩尔01:23:49.273(+00:00:00.000)头|尾01:25:13.548(+000:02:35.735)awk05:47:23.026(+04:24:26.246)切割

大文件的最快解决方案始终是尾部|头部,前提是两个距离:

从文件开头到开始行。我们称之为S从最后一行到文件结尾的距离。是E吗

是已知的。然后,我们可以使用这个:

mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"

多少只是所需的行数。

更多详情请参见https://unix.stackexchange.com/a/216614/79743