在文件包含匹配项的每个文件夹中 Grep 并运行命令

在文件包含匹配项的每个文件夹中 Grep 并运行命令

我正在寻找一种优雅的方法来在 grep 在文件中找到匹配项的每个文件夹中执行命令。与find的标志非常相似-execdir,不同之处在于 find 仅搜索文件夹/文件名,而不搜索其内容。

目前,我正在使用类似的东西

grep -r "pattern" --include=\*.out -l | xargs -L 1 bash -c 'cd `dirname "$0"` && some_script.sh'

有更直接的方法吗?就像是grep ... -execdir

答案1

find能跑grep

find . -type f -name '*.out' \
    -exec grep -q -e 'pattern' {} \; \
    -execdir somescript.sh \;

这将找到名称以.out.对于每个找到的路径名,它将用于grep确定模式是否与文件中的任何行匹配。如果是,-execdir则用于somescript.sh以找到的文件的目录作为其工作目录来执行。

请注意,somescript.sh需要在某个地方可用才能$PATH使其工作,并且脚本将为找到的每个文件执行一次,而不是为每个包含.out匹配文件的目录执行一次。

.out在包含匹配文件的每个目录中仅执行一次脚本:

find . -type d -exec sh -c '
    for dirpath do
        if grep -q -e "pattern" "$dirpath"/*.out 2>/dev/null; then
            ( cd "$dirpath" && exec somescript.sh )
        fi
    done' sh {} +

这用于find查找目录而不是文件。对于找到的批量目录,将执行一个简短的内联 shell 脚本。 shell 脚本尝试将模式与.out每个目录中的所有文件进行匹配(不包括隐藏文件,同时find仍会查找隐藏目录),如果有任何文件匹配,则会启动一个子 shell,在该子 shell 中更改工作目录并somescript.sh执行脚本。

答案2

如果您只想为每个包含至少一个匹配out文件的目录运行该脚本一次,并避免grep每个文件运行一个,您可以这样做:

P="pattern" find . -name '*.out' -type f -exec gawk '
  BEGINFILE {
    dir = FILENAME; sub("/[^/]*$", "", dir)
    if (dir in found) nextfile
  }
  $0 ~ ENVIRON["P"] {
    printf "%s\0", dir
    found[dir]
    nextfile
  }' {} + | xargs -r0 sh -c '
    for dir do
      (cd "$dir" && exec somescript.sh)
    done' sh

这将运行尽可能少的进程,并gawk读取尽可能少的文件(以及每个文件中尽可能少的内容)。

它假定 GNUxargs或兼容的文件名不包含在当前语言环境中不形成有效字符的字节序列。

通过一次 GNUgrep调用(以及 GNU dirname, sort, xargs):

grep -rlZ --include='*.out' pattern . |
  xargs -r0 dirname -z |
  sort -zu |
  xargs -r0 sh -c '
    for dir do
      (cd "$dir" && exec somescript.sh)
    done' sh

与之前的解决方案相反,这将查找所有out文件,甚至是已找到匹配文件的目录中的文件。由于 GNUgrep在 grep 方面比 更有效gawk,因此它可能仍然更有效,具体取决于它最终 grep 到的多余数据量。

相关内容