Grep 忽略模式数组

Grep 忽略模式数组

自从我学会了一些 bash 语法后,我就对它在日常生活中的使用非常热衷。一个著名的命令是grep。如果要 grep 某些内容但忽略几个文件,下面的命令可能会起作用。

grep_ignore=("token_a", "token_b")
grep -rnw . -e "token2" | grep -v <(printf '%s\n' "${grep_ignore[@]}")

如何重现:

  1. 创建一些虚拟文件夹:命令运行mkdir dummy & cd dummy

  2. 创建文件:

    a. file_token_a.txt:命令运行echo "token1 token2" > file_token_a.txt

    b. file_token_b.txt:命令运行echo "token1 token3" > file_token_b.txt

    c. file_token_c.txt:命令运行echo "token2 token3" > file_token_c.txt

命令运行:

grep_ignore=("token_a", "token_b")
grep -rnw . -e "token2" | grep -v <(printf '%s\n' "${grep_ignore[@]}")

预期输出:

./file_token_c.txt:1:token2 token3

给定输出:

./file_token_c.txt:1:token2 token3
./file_token_a.txt:1:token1 token2

答案1

您的尝试存在两个问题:

  1. 你的数组构造有一个错误的逗号,这使得第一个模式token_a,而不是token_a

  2. <(printf '%s\n' "${grep_ignore[@]}")被传递给grep -v搜索的文件,该模式由进程替换的文件描述符字符串组成,如/dev/fd/631,而不是模式列表 - 要从文件(或进程替换)中读取模式,您需要将其作为选项的-f参数

纠正这些:

grep_ignore=("token_a" "token_b")

然后

$ grep -rnw . -e "token2" | grep -vFf <(printf '%s\n' "${grep_ignore[@]}")
./file_token_c.txt:1:token2 token3

-F表示将数组元素视为固定字符串而不是正则表达式)。


或者,至少在 GNU grep 中,您可以使用--exclude(和--include) 将匹配限制到特定文件子集,以完全避免第二次 grep。因此使用上面的例子:

$ grep -rnw . -e "token2"
./file_token_a.txt:1:token1 token2
./file_token_c.txt:1:token2 token3

但给定一个文件名数组模式(注意元素之间用空格而不是逗号分隔):

grep_ignore=("*token_a*" "*token_b*")

然后

$ grep -rnw . -e "token2" "${grep_ignore[@]/#/--exclude=}"
./file_token_c.txt:1:token2 token3

其中数组参数扩展 ${grep_ignore/#/--exclude=}展开如下:

$ printf '%s\n' "${grep_ignore[@]/#/--exclude=}"
--exclude=*token_a*
--exclude=*token_b*

或者你可以使用括号扩展而不是数组:

grep -rnw . -e "token2" --exclude={"*token_a*","*token_b*"}

  1. 尝试一下set -x例如:

     $ grep -rnw . -e "token2" | grep -v <(printf '%s\n' "${grep_ignore[@]}")
     + grep --color=auto -rnw . -e token2
     + grep --color=auto -v /dev/fd/63
     ++ printf '%s\n'
     ./file_token_a.txt:1:token1 token2
     ./file_token_c.txt:1:token2 token3
    

    注意 grep 命令是如何变成的grep --color=auto -v /dev/fd/63?您可以进一步确认它被/dev/fd/63视为模式而不是伪文件,如下所示:

     printf '%s\n' /dev/fd/{61..65} | 
       grep -v <(printf '%s\n' "${grep_ignore[@]}")
    

    (您会看到它/dev/fd/63被过滤掉了)。

答案2

grep -rnw token2 *.txt | grep -E -v "(token_a|token_b)"

在我看来,这是一种比处理数组更简单的方法。

使用 -E 来搜索扩展正则表达式,因此您可以使用 OR 运算符“(token_a|token_b)”。

相关内容