【问题标题】:wc -m seems to stop while loop in bashwc -m 似乎在 bash 中停止 while 循环
【发布时间】:2019-02-28 13:14:30
【问题描述】:

我正在做一个关于 UNIX 的介绍课程——其中一部分是 bash 脚本。我似乎已经理解了这些概念,但在这个特定的问题中,我无法解决这个问题。

我有一个 txt 文件,其中包含 1 列随机用户名。 然后将该 txt 文件用作我的 bash 脚本的参数,理想情况下使用用户名来获取页面并计算该页面上的字符数。如果页面获取成功,则字符数会与用户名一起保存在不同的 txt 文件中。

这是一个代码:

#!/bin/bash
filename=$1

while read username; do
    curl -fs "http://example.website.domain/$username/index.html"
    if [ $? -eq 0 ]
    then
        x=$(wc -m)
        echo "$username $x" > output.txt
    else
        echo "The page doesn't exist"
    fi
done < $filename

现在我遇到的问题是,在一次成功提取后,它会计算字符数,将它们输出到文件中,然后完成循环并退出程序。如果我专门删除“wc -m”位,代码运行得非常好。

问:这应该发生吗,我应该如何解决这个问题以实现我的目标?还是我在其他地方犯了错误?

【问题讨论】:

    标签: bash shell unix wc


    【解决方案1】:

    显示的代码没有按照您的想法(并在您的问题中声明)。

    您的curl 命令获取网络并将其发送到标准输出:您没有保留此信息以供将来使用。然后,您的wc 没有任何参数,所以它开始从标准输入读取。在标准输入中,您有来自$filename 的用户名列表,因此计算的数字不是网络的字符,而是文件的剩余字符。一旦计算完毕,stdin 中就没有任何内容要读取了,因此循环结束,因为它到达了文件的末尾。

    你正在寻找类似的东西:

    #!/bin/bash
    filename="$1"
    
    set -o pipefail
    rm -f output.txt
    while read username; do
        x=$(curl -fs "http://example.website.domain/$username/index.html" | wc -m)
        if [ $? -eq 0 ]
        then
            echo "$username $x" >> output.txt
        else
            echo "The page doesn't exist"
        fi
    done < "$filename"
    

    这里,获取的页面直接馈送到wc。如果curl 失败你不会看到(一系列管道命令的退出代码默认是最后一个命令的退出代码),所以我们使用set -o pipefail 来获取最右边的退出代码的退出代码一个不同于零的值。现在您可以检查是否一切正常,在这种情况下,您可以编写结果。

    我还添加了输出文件的rm 以确保我们不会增加现有文件,并将对输出文件的重定向更改为追加,以避免在每次迭代时重新创建文件并以最后一次迭代的结果(感谢@tripleee 注意到这一点)。

    更新(应大众要求):

    图案:

    <cmd>
    if [ $? -eq 0 ]...
    

    通常是个坏主意。最好去:

    if <cmd>...
    

    所以如果你切换到会更好:

    if x=$(curl -fs "http://example.website.domain/$username/index.html" | wc -m); then
        echo...
    

    【讨论】:

    • 这将导致if 检查来自wc 的退出代码,这将始终为真。无论如何,您要避免显式检查 $? 的反模式
    • @tripleee set -o pipefail 保留 curl 的错误状态。
    • 不错的保存,但你仍然有反模式。
    • @triplee 你是对的,但我不想修改更多他的代码。无论如何,使用pipefail 选项,检查的退出代码应该是失败的命令之一,而不是最后一个命令......或者我错过了什么?
    • 不,这一切都已经整理好了。但是您仍然会在每次迭代时覆盖输出文件。
    【解决方案2】:

    默认情况下,wc 程序(以及您可以在 Linux 上找到的许多其他实用程序)期望其输入在stdin(标准输入)上提供给它,并将其输出提供给stdout(标准输出) )。

    在您的情况下,您希望 wc 对您的 curl 调用的结果进行操作。您可以通过将curl 的结果存储在一个变量中并将变量的内容传递给wc 来实现这一点

    data=$(curl -fs "http://example.website.domain/$username/index.html")
    ...
    x=$(echo "$data" | wc -m)
    

    或者,您可以将整个命令放在一个管道中,这可能会更好(尽管您可能想要set -o pipefail 以捕获来自curl 的错误):

    x=$(curl -fs "http://example.website.domain/$username/index.html" | wc -m)
    

    否则,正如@Dominique 所说,您的wc 将无限期地等待它获得一些输入。

    【讨论】:

      【解决方案3】:

      正如其他人已经指出的那样,只有 wc 会“挂起”,因为它希望您在标准输入上提供输入。

      你似乎在寻找类似的东西

      #!/bin/bash
      filename=$1
      # Use read -r
      while read -r username; do
          if page=$(curl -fs "http://example.website.domain/$username/index.html"); then
              # Feed the results from curl to wc
              x=$(wc -m <<<"$page")
              # Don't overwrite output file on every iteration
              echo "$username $x"
          else
              # Include parameter in error message; print to stderr
              echo "$0: The page for $username doesn't exist" >&2
          fi
      # Note proper quoting
      # Collect all output redirection here, too
      done < "$filename" >output.txt
      

      【讨论】:

        猜你喜欢
        • 2015-08-06
        • 2021-08-18
        • 1970-01-01
        • 1970-01-01
        • 2013-03-12
        • 2021-04-15
        • 2015-03-25
        • 1970-01-01
        • 2014-07-04
        相关资源
        最近更新 更多