使用GNU工具:
find . -type f -exec grep -lZ FIND {} + | xargs -r0 grep -l ME
您可以标准地执行以下操作:
find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;
但这会使每个文件运行两次。为了避免运行那么多grep
s并且仍然可移植,同时仍允许文件名中包含任何字符,可以执行以下操作:
convert_to_xargs() {
sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
{
if (NR > 1) {
printf "%s", line
if (!index($0, "//")) printf "\\"
print ""
}
line = $0
}'
END { print line }'
}
find .//. -type f |
convert_to_xargs |
xargs grep -l FIND |
convert_to_xargs |
xargs grep -l ME
想法是将的输出find
转换为适合xargs的格式(期望空格(SPC / TAB / NL,以及您的语言环境中的其他空格,使用的某些实现xargs
),分隔单词列表,其中可以使用单引号,双引号和反斜杠逃脱空白和彼此)。
通常,您不能对的输出进行后处理find -print
,因为它会用换行符分隔文件名,并且不会转义文件名中的换行符。例如,如果我们看到:
./a
./b
我们没有办法知道这是b
在目录中调用的一个文件a<NL>.
还是在a
和中的两个文件b
。
通过使用.//.
,因为//
不能被作为输出文件路径,否则会出现find
(因为没有这样的东西作为一个空的名字的目录和/
文件名中是不允许的),我们知道,如果我们看到一个包含一条线//
,那么这就是新文件名的第一行。因此,我们可以使用该awk
命令对所有换行符进行转义,但换行符除外。
如果我们以上面的示例为例,find
将在第一种情况(一个文件)中输出:
.//a
./b
哪个awk会转至:
.//a\
./b
因此,xargs
将其视为一个论点。在第二种情况下(两个文件):
.//a
.//b
这awk
将保留原样,所以xargs
看到两个参数。