当 head 完成时,中止curl、xargs 管道中的先前步骤

当 head 完成时,中止curl、xargs 管道中的先前步骤

我正在尝试下载一堆网页,一旦我下载了 N 行 html,我希望整个事情停止。但相反,管道中之前的步骤仍然继续进行。一个例子来看看问题:

for i in /accessories /aches-pains /allergy-hayfever /baby-child /beauty-skincare; do echo $i; sleep 2; done | \
while read -r line; do curl "https://www.medino.com$line"; done \
 | head -n 2

现在,我希望它发出一个请求,然后中止。

但实际发生的情况是这样的:

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0<!DOCTYPE html>
<html lang="en" >
100  4412    0  4412    0     0  12788      0 --:--:-- --:--:-- --:--:-- 12751
curl: (23) Failed writing body (0 != 2358)
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2358    0  2358    0     0   3772      0 --:--:-- --:--:-- --:--:--  3766
curl: (23) Failed writing body (0 != 2358)

( ^ repeats 4 times)

为什么脚本不立即中止,而是继续运行?我不是管道方面的超级专家,所以感觉我在这里错过了一些基本的东西。

答案1

管道的第二部分是while read -r line; do curl ...$line; done。当它运行时:

  1. 在第一次迭代中,shell 将第一个值读取到行中,并运行curl; curl(获取并)输出网页,其中head -n2提取前两行并退出,关闭第二部分和第三部分之间的管道。在您的示例中,curl 将此输出写入至少两个块,因此它在第二次写入时出现错误并失败,即以非零状态退出。

  2. 当一个命令失败时,shell 不会终止大多数命令序列(包括复合命令),因为 shell 经常以交互方式使用,如果每次执行任何命令时 shell 都死掉,迫使您重新登录并重新开始,那将是非常不方便的。运行任何程序时出错。

  3. 因此,shell 将第二个值读入 line 并运行第二个curl,这会立即失败,因为管道已关闭,但 shell 再次继续读取第三行并运行第三个curl,依此类推,直到输入结束导致read失败;由于read位于 的 list-1 部分while,因此它的失败会导致循环终止。

您可以使用以下命令显式测试curl是否失败(然后终止):

generate_values | while read -n line && curl ...$line; do :; done | head -n2

或者你可以设置一个 shell 选项,这样它失败时终止:

generate_values | { set -e; while read -n line; do curl ...$line; done } | head -n2

注意这两种方法都可能运行结束,因为curl仅在写入时报告错误管道被关闭,即在最后一个块之后。如果您的输出限制 ( head -n$n) 在curl #2 的最后一个输出块期间耗尽,则该curl 将“成功”退出,并且shell 将启动curl #3,该curl #3 将在第一次(或唯一一次)写入时失败。

相关内容