与 grep 一起打印文件名会导致 find -exec

与 grep 一起打印文件名会导致 find -exec

我有一个名为的程序foo,我想对通过 find 找到的每个结果执行该程序。所以像这样:

find . -name '*.o' -type f -exec foo {} \;

foo我想 grep某个字符串 bar的所有这些调用的输出。所以我添加这个:

find . -name '*.o' -type f -exec foo {} \; | grep bar

但我丢失了有关匹配项来自哪些文件的原始信息。我尝试添加-fprintf /dev/stderr '%p\n'find命令中,但现在看起来标准输出消失了,因为没有打印 grep 结果。

如何将每个文件名打印到输出,然后打印与该文件相对应的 grep 结果?

或者,如果有某种方法可以使工作-H论证grep也很好,但正如所写的那样,它不起作用,因为我只是从标准输入传递文本,并且grep不知道文件名。我尝试了各种咒语xargs,但也没有任何效果。

答案1

要将每个文件名打印到输出,然后打印与该文件相对应的 grep 结果,您可以将-exec foo {} | grep管道包装在 shell 中:

find . -name '*.o' -type f -print -exec sh -c 'foo "$1" | grep "bar"' sh {} \;

要使-Hgrep 的参数与标准输入一起工作,如果您的版本grep支持--label=您可以执行的选项

find . -name '*.o' -type f -exec sh -c '
  foo "$1" | grep -H --label="$1" "bar"
' sh {} \;

或者(如果您的发现支持+多参数替代\;):

find . -name '*.o' -type f -exec sh -c '
  for f; do foo "$f" | grep -H --label="$f" "bar"; done
' sh {} +

答案2

如何将每个文件名打印到输出,然后打印与该文件相对应的 grep 结果?

1.)使用两个 grep 实例,一个用于打印文件名,另一个用于打印匹配项:

find . -name '*.o' -type f -exec grep -l bar {} \; -exec grep bar {} \;

2.)使 grep 的行为就像是多个文件一样:

find . -name '*.o' -type f -exec grep bar {} /dev/null \;

这并不能解决[..]-exec foo {} \;[...]在管道内运行 grep 的问题,如您的示例所示。

答案3

这很丑陋,但它会对你有用:

find . -name '*.o' -type f -exec sh -c 'foo "$1" | sed "s@^@$1\t@"' _ {} \;

这会将文件名和制表符放在每行的开头。裸下划线是一个填充$0在生成的 shell 中的字符串,文件名保留为$1.现在您可以 grep find 的输出,文件名将出现在输出中。


对于任何包含该字符的文件名,这都会中断@。如果这是一个问题,我们可以增强 shell 脚本:

find . -name '*.o' -type f -exec sh -c '
    r=$(echo "$1" | sed "s/@/\\\\\\\\@/g")
    foo "$1" | sed "s@^@$r\t@"
' _ {} \;

8个反斜杠是通过反复试验确定的......

或者使用 bash(或 ksh),它具有方便的“搜索和替换”参数替换

find . -name '*.o' -type f -exec ksh -c 'foo "$1" | sed "s@^@${1//@/\\\\@}\t@"' _ {} \;

相关内容