如何在 bash 中循环包含 A 和 B 的文件?

How do you loop over files containing A and B in bash?

我正在查找目录中包含“AAA”“BBB”的所有文件。 然后我想 modify/do 填充每个找到的文件。文件名确实包含 spaces.

grep 管道连接到 grep 看起来很简单,但需要 xargs 才能正常工作。

grep 中的多重匹配功能(据我所知)仅适用于“AAA”“BBB”。

我得到的是:

for FILENAME in "$(grep -ilrZ "AAA" ./files/* | xargs -0 grep -ilr "BBB")"
do
  echo "$FILENAME"
  echo "match"
done

但是,这会将列表视为文件(包括换行符)作为单个实体(即“匹配”只会打印一次)。

for 子 shell 中删除双引号意味着每个 space 都是要循环的新事物,这会破坏文件名。

您在第一次调用中使用 grep -Z 也是第二种情况下问题的解决方案。

grep -ilrZ "AAA" ./files/* |
xargs -r0 grep -Zil "BBB" |
xargs -r0 -i echo "{} match"

在第二个 grep 中使用 -r 没有任何意义,因为您只想精确检查第一个 grep.

输出中的文件

如果您只想列出文件,当然,grep -l 已经做到了;据推测,你会想在第二个 xargs.

中做一些更复杂的事情

另一种解决方案可能是

find ./files -type f \
    -exec grep -qi "AAA" {} \; \
    -exec grep -qi "BBB" {} \; \
    -exec sh -c 'for f; do echo "$f matched"; done' _ {} +

它的工作方式是,如果一个 -exec 失败,它失败的文件将被 find 视为失败的谓词,因此其余的谓词将被跳过.到达最终谓词的文件将从所有先前的谓词返回成功。

后者应该在缺少 GNU 扩展 grep -Zxargs -0 等的 non-Linux 平台上工作

如果不明显,findgrep -r 也检查子目录。如果您只想检查当前目录,从第一个 grep 中省略 -r 应该可行。对于 find,您可以在 -type f 之前添加 -maxdepth 1 以仅检查当前目录。

演示:https://ideone.com/ELqKxZ

有关此主题的更多信息,另请参阅 https://mywiki.wooledge.org/BashFAQ/020 and perhaps https://mywiki.wooledge.org/DontReadLinesWithFor

@tripleee 有一个很好的、可靠的答案。另一种选择。假设你的 shell 是 bash 而你的 awk 是 GNU awk

shopt -s globstar    # enable recursive globbing with **
gawk '
    BEGINFILE {a = b = 0}
    /AAA/ {a = 1}
    /BBB/ {b = 1}
    a && b {print FILENAME; nextfile}
' ./files/** 2>/dev/null