![Bash 脚本中的 Grep 无法正常工作](https://linux22.com/image/1138573/Bash%20%E8%84%9A%E6%9C%AC%E4%B8%AD%E7%9A%84%20Grep%20%E6%97%A0%E6%B3%95%E6%AD%A3%E5%B8%B8%E5%B7%A5%E4%BD%9C.png)
我知道互联网上有很多类似的问题,但我找不到针对我的具体问题的解决方案。
我为一项作业编写了这个脚本,我想找出哪些组合在 HTML 中返回 UNLOCKED 结果。
#!/bin/bash
for ((x4=0;x4<=4;x4++)); do
for ((x3=0;x3<=4;x3++)); do
for ((x2=0;x2<=4;x2++)); do
for ((x1=0;x1<=4;x1++)); do
echo $x1 $x2 $x3 $x4
eval curl http://www.artemiosv.info/21.php?p1=$x1;p2=$x2;p3=$x3;p4=$x4
| grep -oP '<BODY>*[\s\S]*</BODY>'
done
done
done
done
将脚本的输出重定向到文件后,返回的结果的第一行是:
0 0 0 0
<HTML><BODY>LOCKED</BODY></HTML>1 0 0 0
<HTML><BODY>LOCKED</BODY></HTML>2 0 0 0
<HTML><BODY>LOCKED</BODY></HTML>3 0 0 0
从 HTML 代码来看,这是卷曲命令,我只想要主文本,在这些行的情况下是 LOCKED。但我使用的正则表达式似乎不起作用,而在脚本之外测试相同的模式则工作正常。HTML 的预期结果应该是 LOCKED、UNLOCKED,我认为你找到了秘密。
为什么grep脚本内部存在此问题,而外部运行正常?我该如何修复它?
答案1
抱歉,我可能忽略了一些东西。
在 shell 中检查你的 curl 命令。删除 grep 并运行它。当我这样做时,我可以看到整个返回结果。我得到了数百次返回结果
<HTML><BODY>LOCKED</BODY></HTML>
我认为这表明该网站阻止了你抓取它。哎呀,只需浏览一下:
http://www.artemiosv.info/21.php?p1=1;p2=2;p3=3;p4=4
它返回一个只显示“已锁定”的页面
关于 grep 的问题,grep 会选择具有字符模式的行。它不会在其中选择子字符串,这就是 sed 之类的工具的用途。我认为 grep 正好满足您的要求。您说它在测试用例中有效,但我无法想象它是如何工作的。
为什么不采取简单的方法,运行网络抓取并将文件保存在您的 PC 中,然后对它们执行 grep/sed/awk 魔法。这是确保您在检索内容时实际获得什么的唯一方法。而且这是您从 grep 或 sed 获得所需内容的更可能的方法。一旦您知道自己在做什么以及拥有什么,管道就很方便。我怀疑这里两种情况都不成立。
答案2
您只需更改正则表达式,以便排除 HTML 标记。由于您使用的是 Perl 兼容正则表达式,因此您可以使用\K
表示“丢弃到目前为止匹配的任何内容”的 来丢弃<BODY>
,以及积极展望丢弃</BODY>
。您还可以使用括号扩展来代替更麻烦的for((var=0;var<=lim;var++))
语法:
for x4 in {0..4}; do
for x3 in {0..4}; do
for x2 in {0..4}; do
for x1 in {0..4}; do
curl "http://www.artemiosv.info/21.php?p1=$x1;p2=$x2;p3=$x3;p4=$x4" 2>/dev/null |
grep -oP '<BODY>\K[\s\S]*(?=</BODY>)'
done
done
done
done
或者,如果您想要 HTML 内容和 4 个变量的值,请使用以下命令:
for x4 in {0..4}; do
for x3 in {0..4}; do
for x2 in {0..4}; do
for x1 in {0..4}; do
printf '%s : %s\n' "$x1 $x2 $x3 $x4" \
"$(curl "http://www.artemiosv.info/21.php?p1=$x1;p2=$x2;p3=$x3;p4=$x4" 2>/dev/null |
grep -oP '<BODY>\K[\s\S]*(?=</BODY>)')"
done
done
done
完毕
请注意,我还删除了,eval
因为它没有任何用处。您只需要重定向 stderr。
最后,您还可以将curl
命令发送到后台,以便可以并行运行多个命令。这些命令不是很繁重,因此您的机器应该能够处理它们,这将大大加快脚本的速度。只需&
在行末添加printf ...
:
printf '%s : %s\n' "$x1 $x2 $x3 $x4" \
"$(curl "http://www.artemiosv.info/21.php?p1=$x1;p2=$x2;p3=$x3;p4=$x4" 2>/dev/null |
grep -oP '<BODY>\K[\s\S]*(?=</BODY>)')" &
但请注意,您尝试下载的网站似乎无法处理多个连续的请求。即使不并行运行,某些请求也会返回空结果(如果手动运行则有效),而并行运行时空结果的数量会成倍增加。您可能需要与老师讨论如何解决这个问题。