Rem*_*i.b 14 bash shell performance awk file
在一个文件夹中,我想打印.txt包含n=27行或更少行的每个文件的名称.我可以
wc -l *.txt | awk '{if ($1 <= 27){print}}'
Run Code Online (Sandbox Code Playgroud)
问题是该文件夹中的许多文件是数百万行(并且行很长),因此命令wc -l *.txt非常慢.原则上,进程可以计算行数,直到找到至少n行,然后继续下一个文件.
什么是更快的替代方案?
仅供参考,我在 MAC OSX 10.11.6
这是一次尝试 awk
#!/bin/awk -f
function printPreviousFileIfNeeded(previousNbLines, previousFILENAME)
{
if (previousNbLines <= n)
{
print previousNbLines": "previousFILENAME
}
}
BEGIN{
previousNbLines=n+1
previousFILENAME=NA
}
{
if (FNR==1)
{
printPreviousFileIfNeeded(previousNbLines, previousFILENAME)
previousFILENAME=FILENAME
}
previousNbLines=FNR
if (FNR > n)
{
nextfile
}
}
END{
printPreviousFileIfNeeded(previousNbLines, previousFILENAME)
}
Run Code Online (Sandbox Code Playgroud)
可称为
awk -v n=27 -f myAwk.awk *.txt
Run Code Online (Sandbox Code Playgroud)
但是,代码在打印完全空文件时失败.我不知道如何解决这个问题,我不确定我的awk脚本是否可行.
使用GNU awk for nextfile和ENDFILE:
awk -v n=27 'FNR>n{f=1; nextfile} ENDFILE{if (!f) print FILENAME; f=0}' *.txt
Run Code Online (Sandbox Code Playgroud)
有任何awk:
awk -v n=27 '
{ fnrs[FILENAME] = FNR }
END {
for (i=1; i<ARGC; i++) {
filename = ARGV[i]
if ( fnrs[filename] < n ) {
print filename
}
}
}
' *.txt
Run Code Online (Sandbox Code Playgroud)
无论输入文件是否为空,这些都将起作用.非gawk版本的注意事项与您当前的其他awk答案相同:
awk 'script' foo bar foo),并且您希望它多次显示,并且awk 'script' foo FS=, bar)gawk版本没有这样的限制.
更新:
为了测试上面的GNU awk脚本和xhienne发布的GNU grep + sed脚本之间的时间,因为她说她的解决方案是faster than a pure awk script我用这个脚本创建了10,000个输入文件,全长0到1000行:
$ awk -v numFiles=10000 -v maxLines=1000 'BEGIN{for (i=1;i<=numFiles;i++) {numLines=int(rand()*(maxLines+1)); out="out_"i".txt"; printf "" > out; for (j=1;j<=numLines; j++) print ("foo" j) > out} }'
Run Code Online (Sandbox Code Playgroud)
然后对它们运行2个命令并得到这些第3次运行时序结果:
$ time grep -c -m28 -H ^ *.txt | sed '/:28$/ d; s/:[^:]*$//' > out.grepsed
real 0m1.326s
user 0m0.249s
sys 0m0.654s
$ time awk -v n=27 'FNR>n{f=1; nextfile} ENDFILE{if (!f) print FILENAME; f=0}' *.txt > out.awk
real 0m1.092s
user 0m0.343s
sys 0m0.748s
Run Code Online (Sandbox Code Playgroud)
两个脚本都生成相同的输出文件.以上是在cygwin上的bash中运行的.我希望在不同的系统上,时序结果可能会略有不同,但差异总是可以忽略不计.
每行打印10行最多20个随机字符(请参阅注释):
$ maxChars=20
LC_ALL=C tr -dc '[:print:]' </dev/urandom |
fold -w "$maxChars" |
awk -v maxChars="$maxChars" -v numLines=10 '
{ print substr($0,1,rand()*(maxChars+1)) }
NR==numLines { exit }
'
0J)-8MzO2V\XA/o'qJH
@r5|g<WOP780
^O@bM\
vP{l^pgKUFH9
-6r&]/-6dl}pp W
&.UnTYLoi['2CEtB
Y~wrM3>4{
^F1mc9
?~NHh}a-EEV=O1!y
of
Run Code Online (Sandbox Code Playgroud)
要在awk中完成所有操作(这会慢得多):
$ cat tst.awk
BEGIN {
for (i=32; i<127; i++) {
chars[++charsSize] = sprintf("%c",i)
}
minChars = 1
maxChars = 20
srand()
for (lineNr=1; lineNr<=10; lineNr++) {
numChars = int(minChars + rand() * (maxChars - minChars + 1))
str = ""
for (charNr=1; charNr<=numChars; charNr++) {
charsIdx = int(1 + rand() * charsSize)
str = str chars[charsIdx]
}
print str
}
}
$ awk -f tst.awk
Heer H{QQ?qHDv|
Psuq
Ey`-:O2v7[]|N^EJ0
j#@/y>CJ3:=3*b-joG:
?
^|O.[tYlmDo
TjLw
`2Rs=
!('IC
hui
Run Code Online (Sandbox Code Playgroud)
如果您使用 GNU grep(不幸的是 MacOSX >= 10.8 提供了 BSD grep ,其-m和-c选项全局作用,而不是每个文件),您可能会发现这个替代方案很有趣(并且比纯awk脚本更快):
grep -c -m28 -H ^ *.txt | sed '/:28$/ d; s/:[^:]*$//'
Run Code Online (Sandbox Code Playgroud)
解释:
grep -c -m28 -H ^ *.txt输出每个文件的名称以及每个文件中的行数,但读取的行数永远不会超过 28 行sed '/:28$/ d; s/:[^:]*$//'删除至少有 28 行的文件,并打印其他文件的文件名替代版本:顺序处理而不是并行处理
res=$(grep -c -m28 -H ^ $files); sed '/:28$/ d; s/:[^:]*$//' <<< "$res"
Run Code Online (Sandbox Code Playgroud)
埃德·莫顿对我的说法提出质疑,即这个答案可能比awk。他在他的答案中添加了一些基准,尽管他没有给出任何结论,但我认为他发布的结果具有误导性,显示了我的答案的更大的挂钟时间,而不考虑用户和系统时间。因此,这是我的结果。
首先是测试平台:
一台运行 Linux 的四核 Intel i5 笔记本电脑,可能非常接近 OP 的系统(Apple iMac)。
一个全新的目录,包含 100.000 个文本文件,平均约 400 行,总共 640 MB,完全保存在我的系统缓冲区中。这些文件是使用以下命令创建的:
for ((f = 0; f < 100000; f++)); do echo "File $f..."; for ((l = 0; l < RANDOM & 1023; l++)); do echo "File $f; line $l"; done > file_$f.txt; done
Run Code Online (Sandbox Code Playgroud)结果:
结论:
在撰写本文时,在类似于 OP 机器的常规 Unix 多核笔记本电脑上,这个答案是给出准确结果的最快的。在我的机器上,它的速度是最快的 awk 脚本的两倍。
笔记:
为什么平台很重要?grep因为我的答案依赖于和之间的并行处理sed。当然,为了获得公正的结果,如果您只有一个 CPU 核心(VM?)或操作系统在 CPU 分配方面存在其他限制,您应该对备用(顺序)版本进行基准测试。
显然,您不能仅根据挂起时间得出结论,因为它取决于请求 CPU 的并发进程数与机器上的内核数。因此我添加了用户+系统计时
这些计时是 20 次运行的平均值,除了命令花费超过 1 分钟的时间(仅运行一次)
对于所有耗时不到 10 秒的答案,shell 处理所花费的时间*.txt不可忽略,因此我预处理了文件列表,将其放入变量中,并将变量的内容附加到我正在基准测试的命令中。
所有答案都给出了相同的结果,除了 1. Tripleee 的答案,argv[0]其结果中包含(“awk”)(在我的测试中修复);2. kvantour 的答案仅列出了空文件(用 修复-v n=27);3. find+sed 答案缺少空文件(未修复)。
我无法测试ctac_ 的答案,因为我手头没有 GNU sed 4.5。它可能是最快的,但也会丢失空文件。
python 答案不会关闭其文件。我必须先做ulimit -n hard。