如何在 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 -Z
、xargs -0
等的 non-Linux 平台上工作
如果不明显,find
和 grep -r
也检查子目录。如果您只想检查当前目录,从第一个 grep
中省略 -r
应该可行。对于 find
,您可以在 -type f
之前添加 -maxdepth 1
以仅检查当前目录。
有关此主题的更多信息,另请参阅 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
我正在查找目录中包含“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 -Z
、xargs -0
等的 non-Linux 平台上工作
如果不明显,find
和 grep -r
也检查子目录。如果您只想检查当前目录,从第一个 grep
中省略 -r
应该可行。对于 find
,您可以在 -type f
之前添加 -maxdepth 1
以仅检查当前目录。
有关此主题的更多信息,另请参阅 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