递归查找与一个或多个模式匹配的文件的最佳方式

时间:2018-02-15 17:20:46

标签: bash grep find

我必须优化一个shell脚本,但是一周之后,我没有成功地对它进行优化。

我必须递归搜索目录中的.c .h和.cpp文件,并检查是否存在这样的字: “float short unsigned continue for signed void default goto sizeof volatile do if static”

words=$(echo $@ | sed 's/ /\\|/g')

files=$(find $dir -name '*.cpp' -o -name '*.c' -o -name '*.h' )

for file in  $files; do
(
        test=$(grep -woh "$words" "$file" | sort -u | awk '{print}' ORS=' ')
        if [ "$test" != "" ] ; then
          echo "$(realpath $file) contains : $test"
        fi
)&
done
wait

我尝试过使用xargs和-exec,但没有结果,我必须保留这种结果格式:

  

/ usr / include / c ++ / 6 / bits / stl_set.h包含:if void

的默认值

也许你可以帮助我(优化它)..

编辑:我必须保留每个单词的一个出现 是的:虽然,因为,不稳定...... NOPE:while,for,for,volatile ...

3 个答案:

答案 0 :(得分:0)

如果您有兴趣查找至少与您的任何模式匹配的所有文件,可以使用globstar

shopt -s globstar
oldIFS=$IFS; IFS='|'; patterns="$*"; IFS=$oldIFS  # make a | delimited string from arguments
grep -lwE "$patterns" **/*.c **/*.h **/*.cpp       # list files with matching patterns
  

globstar

     

如果设置,则在文件名扩展上下文中使用模式“**”   将匹配所有文件和零个或多个目录和子目录。   如果模式后跟'/',则只有目录和   子目录匹配。

答案 1 :(得分:0)

这种方法可以保留您想要的格式,同时不使用find和bash循环:

words='float|short|unsigned|continue|for|signed|void|default|goto|sizeof|volatile|do|if|static|while'
grep  -rwoE --include '*.[ch]' --include '*.cpp' "$words" path | awk -F: '$1!=last{printf "%s%s: contains %s",r,$1,$2; last=$1; r=ORS; delete a; a[$2]} $1==last && !($2 in a){printf " %s",$2; a[$2]} END{print""}'

如何运作

  • grep -rwoE --include '*.[ch]' --include '*.cpp' "$words"路径

    以递归方式搜索以path开头的目录,仅查找名称与globs *.[ch]*.cpp匹配的文件。

  • awk -F: '$1!=last{printf "%s%s: contains %s",r,$1,$2; last=$1; r=ORS; delete a; a[$2]} $1==last{printf " %s",$2} END{print""}'

    此awk命令重新格式化grep的输出以匹配您想要的输出。该脚本使用变量last和数组alast会跟踪我们所处的文件,a包含目前为止看到的字词列表。更详细:

    • -F:

      这告诉awk使用:作为字段分隔符。这样,第一个字段是文件名,第二个字段是找到的字。 (限制:不支持包含:的文件名。)

    • ' $ 1!= last {printf"%s%s:包含%s",r,$ 1,$ 2;最后= $ 1; R = ORS;删除一个;一个[$ 2]}

      每次文件名$1与变量last不匹配时,我们都会为新文件启动输出。然后,我们更新last以包含此新文件的名称。然后,我们删除数组a,然后将关键$2分配给新数组a

    • $1==last && !($2 in a){printf " %s",$2; a[$2]}

      如果当前文件名与前一个文件名相同且之前没有看到当前单词,我们会打印出找到的新单词。我们还将此词$2添加为数组a的关键字。

    • END{print""}

      这将打印出最终换行符(记录分隔符)。

代码的多行版本

对于那些喜欢将代码分散在多行中的人:

grep  -rwoE \
    --include '*.[ch]' \
    --include '*.cpp' \
    "$words" path | 
    awk -F: '
        $1!=last{
            printf "%s%s: contains %s",r,$1,$2
            last=$1
            r=ORS
            delete a
            a[$2]
        }
        $1==last && !($2 in a){
            printf " %s",$2; a[$2]
        }
        END{
            print""
        }'

答案 2 :(得分:0)

您应该可以使用单个grep命令执行大部分操作:

grep -Rw $dir --include \*.c --include \*.h --include \*.cpp -oe "$words"

这将把它放在文件:word格式中,所以剩下的就是改变它以产生你想要的输出。

echo $output | sed 's/:/ /g' | awk '{print $1 " contains : " $2}'

然后,您可以添加| sort -u,以便只为每个文件中的每个单词生成一次。

#!/bin/bash

#dir=.
words=$(echo $@ | sed 's/ /\\|/g')

grep -Rw $dir --include \*.c --include \*.h --include \*.cpp -oe "$words" \
    | sort -u \
    | sed 's/:/ /g' \
    | awk '{print $1 " contains : " $2}'