为了列出具有完全n
行的文件,可以执行
n=5
find . -name "*.txt" | xargs wc -l | awk -v n=${n} -F" " '{if ($1==n) {print $2} }'
但是这个解决方案非常慢,因为它先计算每个文件的行数,然后只选择那些有n
行的行。当行到达n+1
行时计算行和停止的过程会更有效(特别是在处理有大量行的大文件时)。
如何有效地列出具有n
行的文件?
注意,对于特殊情况,每行的大小完全相同,那么可能会做
n=5
sizePerLine=500
find . -name '*.txt' -size $(( ${n} * ${sizePerLine} ))
答案 0 :(得分:3)
我认为以下内容会更快:
find . -name "*.txt" -exec awk -v n="$n" 'FILENAME != prevfile {if(prevfnr==n) print prevfile} {prevfile = FILENAME; prevfnr = FNR; if(FNR>n) {nextfile;}} END{if (FNR==n) {print FILENAME} }' {} +
工作原理:
-exec ... {} +
使用find
为每个文件执行命令,并让它在每次调用时传递多个args awk -v n="$n"
调用awk
并定义名为awk
的{{1}}变量,使其与shell变量n
n
检查当前文件是否与最后一条记录相同,如果没有,则查看前一个文件是否有FILENAME != prevfile {if(prevfnr==n) print prevfile
条记录,如果是,则打印该文件的名称n
使用当前{prevfile = FILENAME; prevfnr = FNR; if(FNR>n) {nextfile;}}
更新prevfile
变量,使用当前FILENAME
更新prevfnr
变量。此外,如果我们当前的文件记录超过FNR
,则跳转到下一个文件而不再处理任何其他文件n
查看最后一个文件是否也包含END{if (FNR==n) {print FILENAME}
条记录有趣的是,我发现这实际上给出了与使用n
的版本不同的结果,尽管我认为这个实际上可能更正确。对于我的目录中最后一行不包含行结束字符wc -l
的文件,将报告行数,不计算最后一行“未终止”行,但此处的解决方案将计算它。
Arg,我没有意识到wc -l
是一个GNU主义。如果我已经限制自己,我们就可以把它变得更加清洁
nextfile
在我看来,POSIX find . -name '*.txt' -exec awk -v n="$n" 'FNR > n {nextfile;} ENDFILE{if (FNR==n) {print FILENAME} }' {} +
有一个很好的快捷方式来跳转到下一个文件,这是该解决方案需要的关键效率
答案 1 :(得分:3)
find . -name '*.txt' -print0 |
xargs -0 -n 1 awk -v n="$n" 'NR>n{exit} END{if (NR==n) print FILENAME}'
甚至更有效地使用GNU awk for ENDFILE:
find . -name '*.txt' -exec \
awk -v n="$n" 'FNR>n{nextfile} ENDFILE{if (FNR==n) print FILENAME}' {} +
上面脚本的主要效率是在你点击大于n
的行号时退出awk工作循环(即跳转到END / ENDFILE部分),而不是等到整个文件已经存在在检查读取的行数之前阅读。
在gawk脚本中显示nextfile
和exit
之间的区别:
$ seq 10 | awk '{print; nextfile} ENDFILE{print "x"} END{print "y"}'
1
x
y
$ seq 10 | awk '{print; exit} ENDFILE{print "x"} END{print "y"}'
1
y
答案 2 :(得分:1)
使用grep
:
n=5
find . -name '*.txt' | xargs grep '.+' -m $((n+1)) -c | grep ':'$n'$'
这告诉grep
仅检查第一行n+1
行,并仅显示包含n
行的文件。
替换xargs ag '\n' -m$n -c
如果你有一个很好的加速 - ag
是一个比grep
更快的搜索者。请注意-m
仅适用于GNU grep;在BSD grep上,它是一个全局选项(改为使用ag
,或者获取GNU grep)。
答案 3 :(得分:1)
使用awk本身:
n=5
find . -name '*.txt' | xargs -n 1 awk -e "{ n++; if (n > $n) {exit 1} } END { if ( n == $n ) print FILENAME}"
如果文件有+5行,这将退出,否则将打印精确的5行。
答案 4 :(得分:1)
您可能会过度复杂化,只需使用for
循环和test
条件进行评估,例如
for f in *.txt; do [ $(wc -l <"$f") -eq "5" ] && echo "$f"; done
这将在当前目录中找到包含.txt
行的所有5
个文件。
答案 5 :(得分:1)
使用perl
:
n=5 find /some/dir -type f -name '*.txt' -exec \
perl -lnE '{ $. == $ENV{n} and eof and say $ARGV } continue { close ARGV if($. == $ENV{n} or eof) }' {} +
答案 6 :(得分:1)
使用grep
和awk
:
$ grep -cr "^" *|awk -F: '$2==6 {print $1}'
故障:
grep -c
计算文件-r
是--recursive "^"
匹配(即计数)行的开头 grep
的输出是:
foo:6
dir/bar:7
awk
使用:
作为字段分隔符和打印文件名(以及相关路径)的文件,其中行数是给定的。答案 7 :(得分:1)
更有效的解决方案是将find
与gawk
一起使用条件ENDFILE
和FNR
。
find . -name '*.txt' -exec awk -v n=$n 'ENDFILE{if(FNR==n) print FILENAME}' {} +
对于任何需要将wc -l
的整个输出传递给另一个解析其输出的进程的解决方案,这会将时间减少一半。也就是说,使用ENDFILE
和nextfile
的其他答案甚至更有效,因为它们允许在达到所需行数时跳到下一个文件。
假设您正在使用Bash&gt; 4.0,可以删除find
的需要,以利用允许递归扩展文件名的globstar
bash选项。只要参数的数量不超过gawk
的{{1}}限制,这应该有效。
ARGC
答案 8 :(得分:1)
使用Bash≥4,这是检查文本文件是否有5行的一种相当有效的方法:
mapfile -n 6 -t lines < file
if (( ${#lines[@]} == 5 )); then
echo "has 5 lines"
else
echo "doesn't have 5 lines"
fi
我们将mapfile
与-n 6
一起使用,以便不超过6行(效率)。
与find
命令一起,我们获得:
find . -name '*.txt' -type f -exec bash -c 'mapfile -n 6 -t lines < "$1"; ((${#lines[@]}==5))' _ {} \; -print
你也可以在bash语句中使用-exec ... +
和一个循环(练习留给读者)。