有没有一种“规范”的方法来做这件事?我一直在使用head -n | tail -1
来完成这个操作,但我一直在想是否有一款Bash工具可以从文件中提取一行(或多行)。
所谓“规范”,即指一个主要功能是执行该操作的程序。
$ time head -11000 [filename] | tail -1
[output redacted]
real 0m0.117s
$ time cut -f11000 -d$'\n' [filename]
[output redacted]
real 0m1.081s
$ time awk 'NR == 11000 {print; exit}' [filename]
[output redacted]
real 0m0.058s
$ time perl -wnl -e '$.== 11000 && print && exit;' [filename]
[output redacted]
real 0m0.085s
$ time sed "11000q;d" [filename]
[output redacted]
real 0m0.031s
$ time (mapfile -s 11000 -n 1 ary < [filename]; echo ${ary[0]})
[output redacted]
real 0m0.309s
$ time tail -n+11000 [filename] | head -n1
[output redacted]
real 0m0.028s
awk 'BEGIN{c=0;print(c)}{c+=length()+1;print(c+1)}' file.txt > file.idx
然后使用tail
读取文件,它实际上会直接seek
到文件中适当的位置!
例如,要获取第1000行:
tail -c +$(awk 'NR=1000' file.idx) file.txt | head -1
对于大文件来说,最快的解决方案始终是使用tail|head,前提是需要知道以下两个距离:
S
E
然后,我们可以使用以下方法:
mycount="$E"; (( E > S )) && mycount="+$S"
howmany="$(( endline - startline + 1 ))"
tail -n "$mycount"| head -n "$howmany"
howmany只是需要的行数计数。
S
和 E
的单位,即字节、字符或行数。 - agcecho "$data" | cut -f2 -d$'\n'
-f3
可以让您得到第三行。cat FILE | cut -f2,5 -d$'\n'
将显示文件中的第2和第5行。(但它不会保持顺序。) - Andriy Makukha根据其他人提到的内容,我希望这个功能在我的bash shell中能够快速实现。
创建一个文件:~/.functions
将以下内容添加到文件中:
getline() {
line=$1
sed $line'q;d' $2
}
然后将其添加到~/.bash_profile
中:
source ~/.functions
现在当你打开一个新的bash窗口时,你可以像下面这样调用函数:
getline 441 myfile.txt
$1
之前,无需将其分配给另一个变量,并且您正在破坏任何其他全局line
。 在Bash中,对于函数变量,请使用local
;但是在这里,如已经说明的那样,可能只需执行sed“$1d;q”“$2”
。(还要注意引用“$2”
。) - tripleee已经有很多不错的答案了,我个人更倾向于使用 awk。如果你使用的是 bash,只需将以下内容添加到你的 ~/.bash_profile
文件中。然后,下次登录时(或者在更新后执行 source ~/.bach_profile
),你就可以通过一个新的神奇函数“nth”来处理你的文件了。
执行此命令或将其放入你的 ~/.bash_profile 文件中(如果使用 bash),并重新打开 bash(或执行 source ~/.bach_profile
)。
# print just the nth piped in line
nth () { awk -vlnum=${1} 'NR==lnum {print; exit}'; }
$ yes line | cat -n | nth 5
5 line
这不是一个bash解决方案,但我发现前面的选择都不能满足我的需求,例如:
sed 'NUMq;d' file
这个程序运行速度已经足够快,但是会挂起数小时且没有任何进展提示。我建议编译这个cpp程序并使用它来找到你想要的行。你可以用g++ main.cpp
来编译,其中main.cpp是下面内容的文件。我得到了a.out并通过./a.out
执行它。
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
string filename;
cout << "Enter filename ";
cin >> filename;
int needed_row_number;
cout << "Enter row number ";
cin >> needed_row_number;
int progress_line_count;
cout << "Enter at which every number of rows to monitor progress ";
cin >> progress_line_count;
char ch;
int row_counter = 1;
fstream fin(filename, fstream::in);
while (fin >> noskipws >> ch) {
int ch_int = (int) ch;
if (row_counter == needed_row_number) {
cout << ch;
}
if (ch_int == 10) {
if (row_counter == needed_row_number) {
return 0;
}
row_counter++;
if (row_counter % progress_line_count == 0) {
cout << "Progress: line " << row_counter << endl;
}
}
}
return 0;
}
使用sed命令打印指定行数的内容,其中行数可以是一个变量:
a=4
sed -e $a'q:d' file
更新 1:在 awk
中找到了更快的方法
133.6百万
以上的一行:rownum='133668997'; ( time ( pvE0 < ~/master_primelist_18a.txt | LC_ALL=C mawk2 -F'^$' -v \_="${rownum}" -- '!_{exit}!--_' ) )
in0: 5.45GiB 0:00:05 [1.02GiB/s] [1.02GiB/s] [======> ] 71%
( pvE 0.1 in0 < ~/master_primelist_18a.txt |
LC_ALL=C mawk2 -F'^$' -v -- ; ) 5.01s user
1.21秒系统 116% CPU
5.353 总计
77.37219=195591955519519519559551=0x296B0FA7D668C4A64F7F=
===============================================
我想质疑“perl”比“awk”更快的观点: f="$( grealpath -ePq ~/master_primelist_18a.txt )"
rownum='133668997'
fg;fg; pv < "${f}" | gwc -lcm
echo; sleep 2;
echo;
( time ( pv -i 0.1 -cN in0 < "${f}" |
LC_ALL=C mawk2 '_{exit}_=NR==+__' FS='^$' __="${rownum}"
) ) | mawk 'BEGIN { print } END { print _ } NR'
sleep 2
( time ( pv -i 0.1 -cN in0 < "${f}" |
LC_ALL=C perl -wnl -e '$.== 133668997 && print && exit;'
) ) | mawk 'BEGIN { print } END { print _ } NR' ;
fg: no current job
fg: no current job
7.58GiB 0:00:28 [ 275MiB/s] [============>] 100%
148,110,134 8,134,435,629 8,134,435,629 <<<< rows, chars, and bytes
count as reported by gnu-wc
in0: 5.45GiB 0:00:07 [ 701MiB/s] [=> ] 71%
( pv -i 0.1 -cN in0 < "${f}" | LC_ALL=C mawk2 '_{exit}_=NR==+__' FS='^$' ; )
6.22s user 2.56s system 110% cpu 7.966 total
77.37219=195591955519519519559551=0x296B0FA7D668C4A64F7F=
in0: 5.45GiB 0:00:17 [ 328MiB/s] [=> ] 71%
( pv -i 0.1 -cN in0 < "${f}" | LC_ALL=C perl -wnl -e ; )
14.22s user 3.31s system 103% cpu 17.014 total
77.37219=195591955519519519559551=0x296B0FA7D668C4A64F7F=
如果您认为这会有所不同(我还没有安装任何一个),我可以使用perl 5.36
甚至perl-6
重新运行测试,但是两者之间存在
7.966秒(mawk2)
与17.014秒(perl 5.34)
两者之间的差距,后者超过前者的两倍以上,似乎很清楚哪个确实更快地获取深层ASCII
文件中的单个行。
This is perl 5, version 34, subversion 0 (v5.34.0) built for darwin-thread-multi-2level
Copyright 1987-2021, Larry Wall
mawk 1.9.9.6, 21 Aug 2016, Copyright Michael D. Brennan
awk
和sed
,我相信还有人可以想出一个Perl单行命令之类的方法 ;) - 0xC0000022L