bash 脚本中带双引号的 grep

grep with double quotation in bash script

我正在尝试从 nginx 配置的白名单 IP 列表中查找 IP。

/tmp/iplist:

11.2.3.4
22.2.3.4

/tmp/whitelist:

"1.2.3.4"
"11.2.3.4"
"1.2.3.44"
"11.2.3.44"

当我 运行 像 grep '"11.2.3.4"' /tmp/whitelist 这样的 grep 时,我可以得到像 "11.2.3.4" 这样的正确答案。

然而,在 bash 脚本中,我无法得到任何答案。
以下是我尝试过的一些模式:

/tmp/findIPs.sh:

#!/bin/bash

for ip in $(cat /tmp/iplist)
do
  grep '"$ip"' /tmp/whitelist
  grep '\"$ip\"' /tmp/whitelist
  grep '\\"$ip\\"' /tmp/whitelist
  x="\"$ip\""
  fgrep '$x' /tmp/whitelist
  grep '$x' /tmp/whitelist
  y="$ip"
  fgrep '$y' /tmp/whitelist
  grep '$y' /tmp/whitelist

而且,这是一个空白的结果。

> bash /tmp/findIPs.sh
>

我漏掉了什么?

您使用的代码的最简单改编是:

for ip in $(cat /tmp/iplist)
do
    grep "$ip" /tmp/whitelist
done

一旦您获得变量中的值,它的双引号就不会被进一步的双引号变量扩展损坏。如果 /tmp/iplist 文件不包含双引号但它们很重要,那么您可以使用:

grep "\"$ip\"" /tmp/whitelist

或者你可以使用这个:

grep \""$ip"\" /tmp/whitelist

(还有两个不对称排列可用)。确保您确实知道它为什么有效是个好主意。如果需要,有一些方法可以使用单引号,但是 "$ip" 部分必须在单引号之外。

您的所有示例都以单引号开始 grep 的模式参数。单引号抑制所有扩展,直到下一个单引号。因此,例如,grep '"$ip"' /tmp/whitelist 正在文件中查找 5 个字符 — "$ip"。在 none 中,它们是不断扩展的变量 ip

如果任何 IP 地址中包含 space,就会出现问题。谨慎使用 for ip in $(cat /tmp/iplist)。通常你会做得更好:

while read -r ip
do
    grep "$ip" /tmp/whitelist
done < /tmp/iplist

另一种方法是使用 grep -Ffgrep:

grep -F -f /tmp/iplist /tmp/whitelist

这并不坚持在 IP 地址周围使用双引号,而是对 /tmp/whitelist 文件进行一次传递(并且对 /tmp/iplist 文件也进行一次传递),这大约是尽可能高效。这将以与之前略有不同的顺序生成行,这可能无关紧要,但您应该注意这一点。

如果必须要双引号(为了避免搜索11.2.3.4时选择11.2.3.44,那么:

grep -F -f <(sed 's/^/"/; s/$/"/' /tmp/iplist) /tmp/whitelist

这使用 process substitution/tmp/iplist 文件的编辑版本传递给 grep 命令。如果您的 shell 中没有进程替换,您可以使用:

sed 's/^/"/; s/$/"/' /tmp/iplist | grep -F -f - /tmp/whitelist

这使得 grep 从标准输入而不是命名文件中读取要匹配的模式列表。如果碰巧 -f - 不起作用(例如,因为您正在使用 macOS 或可能是 BSD 机器在 Mac 上工作),那么 -f /dev/stdin 可能会,或者 -f /dev/fd/0 .

您还可以生成带有双引号的 /tmp/iplist 文件。您可以生成不带双引号的 /tmp/whitelist 文件,然后使用 grep -x 指定完全匹配。

如果你还没有收集到,有很多不同的方法可以做到这一点。