我想递归地搜索目录中具有 .txt 扩展名或没有扩展名的文件,其中有 2 个字符串正在同时搜索。我该如何处理?
例如,该目录中有 5 个文件,其中包含“字符串 1”、“字符串 2”和“字符串 3”。其中 2 个是 .pdf 和 .html,我对它们不感兴趣。其余 3 个是 .txt 文件和/或没有扩展名。我想获取那些包含其中所有字符串并且具有 .txt 扩展名或根本没有扩展名的路径。
答案1
更新了修改后的问题:
find directory -type f \( -name '*.txt' -o ! -name '*.*' \) \
-exec grep -q -F -e 'string 1' {} \; \
-exec grep -q -F -e 'string 2' {} \; \
-exec grep -q -F -e 'string 3' {} \; \
-print
这将在递归调用的目录中搜索directory
带有文件名后缀的常规文件.txt
以及名称中不带点的常规文件。当找到这样的文件时,grep
以类似于我之前描述的方式(见下文)来确定文件中是否存在所有三个字符串。
如果找到字符串,则打印文件的路径名。
或者,使用我的第一部分中的代码(来自下面):
find directory -type f \( -name '*.txt' -o ! -name '*.*' \) -exec sh -c '
for pathname do
if grep -q -F -e "string 1" "$pathname" &&
grep -q -F -e "string 2" "$pathname" &&
grep -q -F -e "string 3" "$pathname"
then
printf "All were found in \"%s\"\n" "$pathname"
fi
done' sh {} +
也可以看看:
修改问题之前的旧答案:
文件名并不重要,因为 Unix 不会从文件名推断文件类型。
要测试某个字符串是否存在于某个名为 的文件中file
,可以这样做
if grep -q -F -e 'some string' file; then
echo 'The string is present'
else
echo 'The string is not present'
fi
这里使用的选项grep
是
-q
:这会变得grep
安静,并且一旦模式匹配,它也会立即终止。它不是提取模式匹配的行,而是以反映是否找到匹配的退出状态退出。这个退出状态就是我在上面的语句中使用的if
。-F
:这使得grep
将模式视为字符串而不是正则表达式。这使得可以测试a * [in the] sky
文本中是否出现类似的字符串,而不必转义其中的特殊字符。-e
:这使得grep
将下一个参数视为用于匹配的模式。这使得可以使用以 开头的模式,而-
不必grep
认为它是命令行选项。
要测试多个字符串,请添加进一步的grep
测试,如下所示:
if grep -q -F -e 'string 1' file &&
grep -q -F -e 'string 2' file &&
grep -q -F -e 'string 3' file
then
echo 'All three string were found in the file'
else
echo 'One or more string was not found in the file'
fi
假设使用具有命名数组(例如bash
)的 shell,还可以将字符串存储在数组中并执行如下循环:
strings=( 'string 1' 'string 2' 'string 3' )
found=true
for string in "${strings[@]}"; do
if ! grep -q -F -e "$string" file; then
found=false
break
fi
done
if "$found"; then
echo 'All strings were found'
else
echo 'Not all strings were found'
fi
这会迭代字符串,如果其中一个是不是找到(注意它!
否定了测试结果grep
),然后将变量found
设置为false
并退出循环(我们不需要测试进一步的字符串)。
然后我们测试是否$found
是true
orfalse
并根据该测试的结果采取行动。
上面的 shell 代码重写为/bin/sh
(没有命名数组):
set -- 'string 1' 'string 2' 'string 3'
found=true
for string do
if ! grep -q -F -e "$string" file; then
found=false
break
fi
done
if "$found"; then
echo 'All strings were found'
else
echo 'Not all strings were found'
fi
答案2
搜索多个字符串是 awk 的工作,而不是 grep 的工作:
find directory -type f \( -name '*.txt' -o ! -name '*.*' \) \
-exec awk '
index($0,"string 1"){x=1}
index($0,"string 2"){y=1}
index($0,"string 3"){z=1}
x && y && z { f=1; exit }
END { exit !f }
' {} \; \
-print
请注意,在上面的 awk 中,每个输入文件仅调用一次,而不是每个输入文件的每个字符串调用一次。编写一个脚本来查找任意数量的字符串也很简单,而不是一次硬编码一行,并且仍然只为每个文件调用 awk 一次,例如:
find directory -type f \( -name '*.txt' -o ! -name '*.*' \) \
-exec awk '
BEGIN {
totReqd = split("string 1 \
string 2 \
string 3", strings, /[[:space:]]+\n[[:space:]]+/)
}
{
for (idx in strings) {
if ( index($0,strings[idx]) ) {
totFound++
delete strings[idx]
}
}
}
totFound == totReqd { f=1; exit }
END { exit !f }
' {} \; \
-print
上述两项都未经测试,但如果不完全正确,应该很接近。它们可以进一步轻松修改以一次操作多个文件。
答案3
编辑如下更新的问题,您可以使用 -e 选项 grep 2 个模式。您正在查看的文件不需要扩展名,只需使用通配符,这样您的语句就会看起来像这样
grep -e "word1" -e "word 2" /your/folder/*
或者对于包含单词“txt”的文件中的 3 个字符串也可以这样
grep 'word1\|word2\|word3' /your/folder/*txt*
尝试看看你会得到什么
如果您想在同一行中找到两个字符串,您可以这样做
grep "word 1" /your/folder/* | grep "word 2"
这会将第一个 grep 的结果通过管道传输到另一个具有不同字符串的结果。或执行以下操作
grep -e 'word1.*word2\|word2.*word1' /your/folder/*
所以它会首先查找 word1,然后查找 word2,反之亦然