如何显示多个请求中curl的单独退出状态?

Inv*_*999 1 url bash curl

我的问题很简单 -当curl执行多个请求时,有没有办法显示每个URL的curl的单独退出状态?

让我们假设我需要检查站点a.comb.comc.com查看它们:

  • HTTP 返回码
  • 如果HTTP返回码是000,我需要显示curl的exit code

注意 - a.comb.comc.com在此代码/问题中用作示例。在真实的脚本中,我确实有一个有效的 URL 列表 - 其中超过 400 个具有非重叠模式 - 并且它们返回各种 HTTP 代码 - 200/4xx/5xx 以及 000。

000 是curl 无法建立连接的情况,但提供退出代码以了解阻止其建立连接的原因。就我而言,还有许多退出代码 - 6、7、35、60。

我尝试运行以下代码

unset a
unset rep
a=($(curl -s --location -o /dev/null -w "%{response_code}\n" {https://a.com,https://b.com,https://a.com}))
rep+=("$?")
printf '%s\n' "${a[@]}"
echo
printf '%s\n' "${rep[@]}"
Run Code Online (Sandbox Code Playgroud)

虽然上述代码返回每个单独请求的 HTTP 返回代码,但仅显示最后一个请求的退出代码。

000
000
000

60
Run Code Online (Sandbox Code Playgroud)

当我提供多个 URL 进行卷曲时,我确实需要能够记录单独的退出代码。这个问题有解决方法/解决方案吗?

一些附加信息:目前,我将所有 URL 放入一个数组中,并通过它运行一个循环,分别检查每个 URL。然而,浏览 400 个 URL 需要 1-2 小时,我需要以某种方式加快该过程。我确实尝试将-Z 与curl 一起使用。虽然它确实使进程加快了约 40-50%,但没有帮助,因为除了仅显示上述最后的退出状态之外,在本例中,退出状态始终显示为 0,这是不正确的。

PS 我愿意使用任何其他命令行工具,如果它可以解决上述问题 - 并行检查 10s/100s 的 URL 并记录其 HTTP 代码,如果无法建立连接 - 记录其他信息,例如curl的 Exit代码可以。

谢谢。

Kam*_*ski 5

分析

\n

退出代码被命名为“退出代码”,因为它是在命令退出时返回的。如果你只运行一个,curl那么它只会退出一次。

\n

curlcurl当给定一个或多个 URL 时,可能会提供一种检索代码的方法,该代码相当于单独处理当前 URL的退出代码;它将与%{response_code}您使用的类似。不幸的是,似乎没有这样的功能(但是;也许添加它)。要获得 N 个退出代码,您需要 N 个curl进程。你需要运行这样的N次:

\n
curl \xe2\x80\xa6 ; echo "$?"\n
Run Code Online (Sandbox Code Playgroud)\n

我知道你的 N 大约是 400,你循环尝试了这个,花了几个小时。好吧,生成 400curl秒(echo如果不是内置的,即使是 400 秒echo;如果需要的话,即使是 400 个(子)炮弹)也不是那么耗时。罪魁祸首是你同步运行所有这些运行所有这些(不是吗?)。

\n
\n

简单循环及其问题

\n

可以异步循环和运行代码片段:

\n
for url in \xe2\x80\xa6 ; do\n   ( curl \xe2\x80\xa6 ; echo "$?" ) &\ndone\n
Run Code Online (Sandbox Code Playgroud)\n

但这种简单的方法存在几个问题:

\n
    \n
  1. 你不能轻易限制同时运行的数量curl,没有队列。就性能和可用资源而言,这可能非常糟糕。
  2. \n
  3. curl来自两个或多个命令(例如,来自两个或多个命令)的并发输出可能会交错,可能是在中线。
  4. \n
  5. 即使每个命令的输出单独看起来不错,curl或者echo来自另一个子 shell 的输出可能会在curl其相应的echo.
  6. \n
  7. 无法保证较早调用的子 shell 在稍后调用的子 shell 之前开始(或结束)打印。
  8. \n
\n
\n

parallel

\n

正确的工具是parallel. 该工具的基本变体(来自moreutils,至少在 Debian 中)解决了 (1)。在某些情况下它可能解决(2)。无论如何,这都是无关紧要的,因为这个变体不能解决(3)或(4)。

\n

GNUparallel解决了所有这些问题。

\n
    \n
  • 它通过设计解决了(1)。

    \n
  • \n
  • 它用它的--group选项解决了(2)和(3):

    \n
    \n

    --group
    \n分组输出。每个作业的输出都分组在一起,并且仅在命令完成时才打印。首先是 Stdout(标准输出),然后是 stderr(标准错误)。[\xe2\x80\xa6]

    \n
    \n

    来源

    \n

    这是默认值,因此通常您不必显式使用它。

    \n
  • \n
  • 它用它的选项解决了(4)--keep-order

    \n
    \n

    --keep-order
    \n -k
    \n保持输出顺序与输入顺序相同。通常,作业完成后就会立即打印作业的输出。[\xe2\x80\xa6]-k仅影响打印输出的顺序,而不影响作业运行的顺序。

    \n
    \n

    来源

    \n
  • \n
\n

在 Debian 中,GNUparallel位于名为parallel. 这个答案的其余部分使用 GNU parallel

\n
\n

基本解决方案

\n
<urls parallel -j 40 -k \'curl -s --location -o /dev/null -w "%{response_code}\\n" {}; echo "$?"\'\n
Run Code Online (Sandbox Code Playgroud)\n

其中urls是带有 URL 的文件,-j 40意味着我们最多允许 40 个并行作业(根据您的需求和能力进行调整)。{}在这种情况下,嵌入shell 代码是安全的。这是这个答案中明确提到的一个例外:永远不要嵌入{}shell 代码!

\n

输出会像

\n
404\n0\n200\n0\n000\n7\n\xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,单引号字符串是 shell 代码。您可以在其中实现一些逻辑,因此0永远不会打印退出代码。如果我是你,我无论如何都会在同一行的开头位置打印它:

\n
<urls parallel -j 40 -k \'\n   out="$(\n      curl -s --location -o /dev/null -w "%{response_code}" {}\n   )"\n   printf "%s %s\\n" "$?" "$out"\'\n
Run Code Online (Sandbox Code Playgroud)\n

现在即使有一些curl在打印之前被手动终止,您也会在第一列中得到一些内容。这对于解析很有用(我们将返回它)。例子:

\n
0 404\n0 200\n7 000\n\xe2\x80\xa6\n143 \n\xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

其中143意味着curl被终止(请参阅进程终止时的默认退出代码)。

\n
\n

带数组

\n

如果您的 URL 位于名为 的数组中urls,请避免使用以下语法:

\n
parallel \xe2\x80\xa6 ::: "${urls[@]}"    # don\'t\n
Run Code Online (Sandbox Code Playgroud)\n

parallel是一个外部命令。如果数组足够大,那么您将命中argument list too long。使用这个代替:

\n
printf \'%s\\n\' "${urls[@]}" | parallel \xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

它会起作用,因为在 Bash 中printf是内置的,因此之前的所有内容|都由 Bash 内部处理。

\n

要从urls数组到a数组rep,请按以下步骤操作:

\n
unset a\nunset rep\nwhile read -r repx ax; do\n   rep+=("$repx")\n   a+=("$ax")\ndone < <(printf \'%s\\n\' "${urls[@]}" \\\n         | parallel -j 40 -k \'\n              out="$(\n                 curl -s --location -o /dev/null -w "%{response_code}" {}\n              )"\n         printf "%s %s\\n" "$?" "$out"\')\nprintf \'%s\\n\' "${a[@]}"\necho\nprintf \'%s\\n\' "${rep[@]}"\n
Run Code Online (Sandbox Code Playgroud)\n
\n

笔记

\n
    \n
  • 如果我们在第二列中生成退出代码(这更容易,您不需要像这样的辅助变量out)并相应地调整我们的read,所以它是read -r ax repx,那么一行<empty ax><space>143将保存143到,ax因为read忽略前导空格(it\'很复杂)。通过颠倒顺序,我们可以避免代码中出现错误。像这样的行143<space><empty ax>由 正确处理read -r repx ax

    \n
  • \n
  • 您有望在几分钟内检查 400 个 URL。持续时间取决于您允许并行运行的作业数量 ( parallel -j \xe2\x80\xa6),还取决于:

    \n
      \n
    • 服务器响应速度有多快;
    • \n
    • curl下载数据量和速度;
    • \n
    • --connect-timeout和等选项--max-time(考虑使用它们)。
    • \n
    \n
  • \n
\n