grep 在 txt 文件或没有扩展名的文件中,要求文件中存在多个字符串

grep 在 txt 文件或没有扩展名的文件中,要求文件中存在多个字符串

我想递归地搜索目录中具有 .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并退出循环(我们不需要测试进一步的字符串)。

然后我们测试是否$foundtrueorfalse并根据该测试的结果采取行动。

上面的 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,反之亦然

相关内容