bash 使用变量的脚本 grep 无法找到实际存在的结果

bash script grep using variable fails to find result that actually does exist

我有一个 bash 脚本,它遍历 link 的列表,每个 link 向下卷曲一个 html 页,greps 特定的字符串格式(语法为:CVE-####-####),删除周围的 html 标签(这是一种一致的格式,无需特殊情况处理),在变更日志文件中搜索结果字符串 ID,以及最后根据是否找到字符串 ID 来做一些事情。

找到的字符串ID被设置为一个变量。问题是,当对变量进行 grepping 时没有结果,即使我肯定知道某些 ID 应该有结果。这是脚本的相关部分:

for link in $(cat links.txt); do
    curl -s "$link" | grep 'CVE-' | sed 's/<[^>]*>//g' | while read cve; do
        echo "$cve"
        grep "$cve" ./changelog.txt
    done
done

如果我在 grep 命令中对已知 ID 进行硬编码,脚本会按预期找到该 ID 和 returns 东西。我已经尝试了很多对这个变量的 grepping 变体(例如导出它并进行命令扩展,cat'ing 变更日志和管道到 grep,通过 curl 链的命令扩展直接设置变量,变量周围的单引号和双引号,半个打其他东西)。

我是否遗漏了一些与 curl | grep | sed 链的输出变量有细微差别的东西?当它被回显到标准输出或 >> 到文件时,一切看起来都很好(没有奇怪字符或回车 returns 等的单个 ID)。

任何提示或替代解决方案将不胜感激。谢谢!

仅供参考:

OSX:$bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14)

编辑:

我正在卷曲的 html 文件中塞满了 return。 运行 带有 set -x 的脚本很有帮助,因为它揭示了被 grep 的真实字符串:$'CVE-2011-2716\r'.

+ read -r link
+ curl -s http://localhost:8080/link1.html
+ sed -n '/CVE-/s/<[^>]*>//gp'
+ read -r cve
+ grep -q -F $'CVE-2011-2716\r' ./kernelChangelog.txt

也从另一个角度调查,打开 vim 中的卷曲文件显示 ^M 并执行 printf %s "$cve" | xxd 还显示了附加到 grep 变量的回车 return 十六进制代码 0d。依赖 'echo' stdout 是一种错误的诊断方式。使用有效的 CVE-####-#### 编写一个简单的 html 页面,然后添加回车 return(在 vim 插入模式下只需键入 ctrl-v ctrl- m 插入回车 return) 将创建一个示例文件,但上面的原始脚本片段失败。

这是我应该弄清楚的非常标准的字符串清理内容。解决方案是删除回车 returns,通过管道传输到 tr -d '\r' 是一种方法。我不确定这一系列步骤在 SO 上是否有特定的重复,但无论如何这是我现在的工作脚本:

while read -r link; do
  curl -s "$link" | sed -n '/CVE-/s/<[^>]*>//gp' | tr -d '\r' | while read -r cve; do
    if grep -q -F "$cve" ./changelog.txt; then
      echo "FOUND: $cve";
    else
      echo "NOT FOUND: $cve";
    fi;
  done
done < links.txt

它应该是这样的:

# First: Care about quoting your variables!

# Use read to read the file line by line
while read -r link ; do
    # No grep required. sed can do that.
    curl -s "$link" | sed -n '/CVE-/s/<[^>]*>//gp' | while read -r cve; do
        echo "$cve"
        # grep -F searches for fixed strings instead of patterns
        grep -F "$cve" ./changelog.txt
    done
done < links.txt

HTML 文件可以在行尾包含回车符 returns,您需要将其过滤掉。

curl -s "$link" | sed -n '/CVE-/s/<[^>]*>//gp' | tr -d '\r' | while read cve; do

请注意,无需使用 grep,您可以在 sed 命令中使用正则表达式过滤器。 (您也可以在 sed 中使用 tr 命令删除字符,但是对 \r 执行此操作很麻烦,因此我转而使用 tr