我正在寻找一种优雅的方法来在 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 到的多余数据量。