如何通过正则表达式和行数来grep文件

时间:2018-11-20 14:34:56

标签: bash

我只需要grep不包含use Test::More tests => 1;字符串并且具有多于10字符串的文件。该怎么做?

用于打印不匹配文件名的典型解决方案是使用grep -L标志,用于计数行号的典型解决方案是使用wc -l。但是如何将它们结合起来?

grep -rL "use Test::More tests => 1;" t | wc -l

在grep输出中仅显示结果数量。

3 个答案:

答案 0 :(得分:1)

grep -L将列出不包含搜索字符串的文件。因此,grep -L是您解决方案的基本组成部分。但是,通过将结果传递给wc -l,您仅在计算不包含搜索字符串的所有文件。这不是您想要的那样。相反,您只想列出没有搜索字符串且行数超过10行的文件。考虑以下代码:

grep -rL "use Test::More tests => 1;" t  | xargs wc -l | awk '$1 > 10 {print $2}' 

这里最有趣的命令是xargs,它将标准输入中的输出作为参数传递给下一个命令:wc -l。现在,wc -l将为您提供行数列表和文件名。这将通过管道传递到awk,awk将选择第一列值大于10的所有行,并仅显示第二列。

您可能会发现单独运行命令以查看传递到下一个管道的输出很有用:

grep -rL "use Test::More tests => 1;" t  | xargs echo

grep -rL "use Test::More tests => 1;" t  | xargs wc -l

grep -rL "use Test::More tests => 1;" t  | xargs wc -l | awk '$1 > 10 '

然后将它们放在一起:

grep -rL "use Test::More tests => 1;" t  | xargs wc -l | awk '$1 > 10 {print $2}'

答案 1 :(得分:1)

您可以在流程替换中使用find来运行循环:

while IFS= read -d '' -r file; do
   grep -Fq 'use Test::More tests => 1;' "$file" && 
   (( $(wc -l < "$file") >= 10 )) && echo "$file"
done < <(find . -type f -print0)

此代码处理带有空格,换行符或glob字符的文件名。

答案 2 :(得分:0)

TL; DR:

awk 'FNR==1 { found=0 }
     /use\s+Test::More\s+tests\s*=>\s*1\s*;/ { found=1; }
     FNR > 10 { if ( found ) { print FILENAME; nextfile } }' t/*

  

使用和不使用grep来破坏它。

要仅获取多于10行的文件,请执行以下操作:

awk 'FNR==11 { print FILENAME; nextfile; }' *

FNR是“记录文件号”,即我们在文件的哪一行。如果是11,则超过十行,因此打印FILENAME并移至下一个文件。

您可以使用

将没有搜索字符串的文件列表保存到数组中
declare -a lst=( $( grep -rL "use Test::More tests => 1;" t ) )

然后您可以使用

报告十行以上的内容
awk 'FNR==11 { print FILENAME; nextfile; }' "${lst[@]}"

尽管我建议您不要过于僵化-有时人们会用手指摸索或对齐东西等,所以可以这样尝试:

declare -a lst=( $( grep -rLE "use\s+Test::More\s+tests\s*=>\s*1\s*;" t ) )
awk 'FNR==11 { print FILENAME; nextfile; }' "${lst[@]}"

您可以通过子呼叫在一行中完成所有操作,就像这样:

awk 'FNR==11 { print FILENAME; nextfile; }' $( grep -rLE "use\s+Test::More\s+tests\s*=>\s*1\s*;" t )

这也避免了不必要的无关执行。如果您想真正地减少它,我们可以将它们全部放在一个awk中,但是如果我们需要遍历一个以上的子目录,则应该使用grepfind无论如何。否则,

  

如果您仅搜索t目录中的文件,而不是其子目录-

awk 'FNR==1 { found=0 }
     /use\s+Test::More\s+tests\s*=>\s*1\s*;/ { found=1; }
     FNR > 10 { if ( found ) { print FILENAME; nextfile } }' t/*

您可以优化此范围,例如,正在检查的所有文件都具有*.pl之类的名称,这样可以避免尝试读取目录和其他类似的丑陋内容。同样,这可能会因文件名奇/零而混淆。

  

但是 IF 您真正想要的是包含十个以上 distinct 行的文件,这些行中没有令牌字符串,然后将awk更改为-

awk '1 == FNR { cnt=0; found=0; }
     hit[$0]  { next; }
     /use\s+Test::More\s+tests\s*=>\s*1\s*;/ { found=1; }
     { hit[$0]=1; cnt++;
       if ( 10 < cnt ) { print FILENAME; nextfile; }
     }
    ' t/*

是的,如果您愿意,可以将所有内容压缩成一行, ew ,不要,大声笑。