我正在寻找一种方法来搜索同一文件中存在两个单词实例的文件。到目前为止,我一直在使用以下内容来执行搜索:
find . -exec grep -l "FIND ME" {} \;
我遇到的问题是,如果“FIND”和“ME”之间没有一个空格,则搜索结果不会生成该文件。如何调整文件中同时存在“FIND”和“ME”而不是“FIND ME”的前搜索字符串?
我用的是AIX。
答案1
使用 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。为了避免运行那么多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 }'
}
export LC_ALL=C
find .//. -type f |
convert_to_xargs |
xargs grep -l FIND |
convert_to_xargs |
xargs grep -l ME
这个想法是将 的输出转换find
为适合 xargs 的格式(需要一个空格(语言环境中的 SPC/TAB/NL C
,其他语言环境中的 YMMV)分隔的单词列表,其中单引号、双引号和反斜杠可以转义空格,并且每个其他)。
通常,您无法对 的输出进行后处理find -print
,因为它用换行符分隔文件名,并且不会转义文件名中找到的换行符。例如,如果我们看到:
./a
./b
我们无法知道它是b
在被调用的目录中调用的一个文件,还是在当前目录中a<NL>.
调用的两个文件。a
b
通过使用.//.
, 因为//
不能以其他方式出现在作为输出的文件路径中find
(因为不存在具有空名称的目录,并且/
不允许在文件名中出现),我们知道如果我们看到包含 的行//
,那么那就是新文件名的第一行。因此,我们可以使用该awk
命令来转义除这些行之前的所有换行符。
如果我们采用上面的示例,find
将在第一种情况下输出(一个文件):
.//a
./b
哪个 awk 转义到:
.//a\
./b
因此,这xargs
将其视为一个论点。在第二种情况下(两个文件):
.//a
.//b
哪个awk
会保持原样,所以xargs
看到两个参数。
您需要LC_ALL=C
so sed
(awk
以及 的一些实现xargs
)适用于任意字节序列(即使在用户的区域设置中不形成有效字符),以简化空白的仅定义 SPC 和 TAB,并避免不同实用程序对编码包含反斜杠编码的字符进行不同解释的问题。
答案2
如果文件位于单个目录中,并且它们的名称不包含空格、制表符、换行符、*
、?
或[
字符,并且不以-
或开头.
,则这将获得包含 ME 的文件列表,然后将范围缩小到还包含 FIND。
grep -l FIND `grep -l ME *`
答案3
你awk
也可以运行:
find . -type f -exec awk 'BEGIN{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; END{if (cx > 0 && cy > 0) print FILENAME}' {} \;
它使用cx
和分别cy
对匹配 和 的行进行计数。在该块中,如果两个计数器 > 0,则打印. 这会更快/更有效:FIND
ME
END
FILENAME
gnu awk
find . -type f -exec gawk 'BEGINFILE{cx=0; cy=0}; /FIND/{cx++}
/ME/{cy++}; ENDFILE{if (cx > 0 && cy > 0) print FILENAME}' {} +
答案4
看看已接受的答案,它似乎比需要的更复杂。find
和grep
的GNU 版本xargs
支持 NULL 终止字符串。很简单:
find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l ME
您可以修改find
命令来过滤您想要的文件,并且它适用于包含任何字符的文件名;无需增加解析的复杂性sed
。如果您想进一步处理文件,请--null
在最后添加另一个grep
find . -type f -print0 | xargs -0 grep -l --null FIND | xargs -0 grep -l --null ME | xargs -0 echo
并且,作为一个函数:
find_strings() {
find . -type f -print0 | xargs -0 grep -l --null "$1" | xargs -0 grep -l "$2"
}
显然,如果您没有运行这些工具的 GNU 版本,请使用已接受的答案。