【问题标题】:How to pipe while loop input to the encapsulated commands after the "do"如何在“do”之后将while循环输入传递给封装的命令
【发布时间】:2014-11-29 19:58:34
【问题描述】:

我的意图是在下载文件时获取(然后修改)cURL 输出。

    curl -# -L --user-agent 'Mozilla/5.0' -o "`echo -e "$filename"`" "$url" 2>&1 |
    while true
    do
        read <&1 line1 line2
        echo $line2
            if [ "`echo $line2 | grep -o "#"`" = "#" ] ; then
                echo "downloading..."
                break
            elif [ "`echo $line2 | grep -o "curl"`" = "curl" ] ; then
                echo $line2
                break
            else
            echo "stderr still empty"    
            fi
    done

问题是 cURL 输出(发送到 stderr)是动态的,所以我认为我必须使用 while true 直到可以读取某些内容,因为如果我在 curl 之后立即通过 while read 进行管道传输,管道是空的。

知道我想直接发送 curl 输出以读取上述构造中的输入。

而且我真的不想使用任何临时文件(我同意这可以很容易地解决这个问题:))

编辑:这是存储在日志中的 stderr 上的 curl 动态 curl 输出:

 
######################################################################## 100.0%
                                                                           0.2%
                                                                           0.5%
                                                                           1.1%
#                                                                          1.6%
#                                                                          2.6%
##                                                                         3.3%
###                                                                        4.6%
####                                                                       6.1%
####                                                                       6.5%
####                                                                       6.8%
#####                                                                      7.3%
#####                                                                      7.7%
#####                                                                      8.0%
#####                                                                      8.2%
######                                                                     8.7%

【问题讨论】:

  • ...但是read 应该等待缓冲区填满。我可以将 curl 输出直接传送到 while read 循环,没问题。也许你可以发布你的错误?
  • ...and what you want achieve? (因为 grepping curl 的进度条不是最好的主意...)
  • 我要做的是,首先检查下载是否正确开始,如果curl返回错误,然后(为了简化)将进度条打印为绿色或将错误打印为红色。跨度>
  • 我刚刚意识到:在日志的每一行末尾都有一个 ^M (CTRL-M)
  • 我设法用sed $'s/\r/\\\n/g'tr '\r' '\n' &lt; log &gt; log2 转换它们

标签: bash curl while-loop pipe stderr


【解决方案1】:

如果您想在 curl 下载时报告“正在下载...”,您可能需要执行以下操作(注意 - 有点高级的 bash 黑客攻击):

curl "$url" -o "$outfile" -vs > >( # redirect stdout to sub-shell, note the space
  # parse curl's "verbose" output which tells us what is happening
  # while we use -s to silence the standard progress bar which is useless for this
  while read line; do # read verbose progress messages
    if grep -q "Connected" <<<"$line"; then # wait for curl to connect
      echo "Downloading..."
      break
    fi
  done
  cat > /dev/null # make sure to consume all stdin if we break early, to prevent "broken pipe"
) 2>&1 || echo "Error!"

我使用进程替换将输出定向到子 shell 的原因是为了不丢失 curl 退出代码,如果出现错误,该代码将非零,允许我使用 bash 逻辑评估来报告错误(或我可以从$? 捕获它并检查它——curl 有非常详细的退出代码报告,所以我可以从退出代码中找出问题所在。详情请咨询man curl)。

还要注意2&gt;&amp;1 的位置,它将标准错误流重定向到标准输出流,以便进程替换可以获取通常发布到错误流的 curl 进度消息。此重定向必须在 stdout 捕获之后进行,否则它只会创建 stdout 的副本,而 subshel​​l 将一无所获。这在 Bash 手册中关于流复制的部分有更好的解释。

关于您的代码的说明

您在上面的代码中有几个问题:

*) 而不是

... -o "`echo $filename`" ...

你应该这样做

... -o "$filename" ...

当您想要做的只是让 bash 将 $filename 扩展为 -o 参数的值时,确实没有理由进行额外的间接操作。

*) 这毫无意义:

read <&1

文件描述符1 是标准输出——你真的不想从中读取。如果你想从标准输入读取,没有什么可以重定向的 - read 默认从标准输入读取。

*) 这不是好的做法:

if [ "`echo $var | grep -o "value"`" == "value" ]; then

出于多种原因 - 首先,grep -o 几乎毫无用处,尤其是对于简单的文本搜索,因为它只会输出您想要查找或不查找的内容(取决于是否找到了该值),而这些内容正好映射到 @ 987654334@ 退出代码为 0 或 1。因此,最好简单地执行 if echo $var | grep -q "value"; then。如果grep 成功,则此表达式将成功,否则将失败。 -q 用于使输出静音,因为您不需要它(只要它成功与否)。此外,如果您在变量中有文本,则使用 echo 将其转换为 grep 只是很烦人(它会打开一个子 shell 和分叉之类的东西),而不是说 grep -q "value" &lt;&lt;&lt;"$var"

最后,关于报告curl 是否正在下载,我个人认为curl 进度指示器已经足够好,但如果不是,则使用-s 将其静音。

【讨论】:

  • 感谢@Guss,这是一个非常有启发性的答案!你的最后一部分代码可以解决问题,是的,但我不喜欢sleep 1,因为如果互联网连接有点慢,这个结构可能无法正常工作......这就是我打算获取输出的原因.我不知道为什么,但是当我将 curl 输出通过管道传输到 tr 或 sed 时,输出始终为空!你对此有什么想法吗?
  • 我忘了提到你的代码不起作用的主要原因 - curl 进度通知是在标准错误流(文件描述符 2)上发送的,所以连接到标准输出的管道不会查看进度消息。
  • 是的,没错,但我确实为我的 curl 命令复制了 fd 2 on 1!
  • 请看我更新的答案 - 我认为它有你需要的一切。
  • 我需要一些时间来处理和测试所有这些,但它看起来很不错,非常感谢 Guss :) 我正在考虑使用 -v 和 -s 选项来获取输出和从中进行测试。但是为什么当进度条打开时我不能正确地输出输出?这仍然是一个谜......
猜你喜欢
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-02
相关资源
最近更新 更多