【问题标题】:Bash script: Show progress indicator until while loop completesBash脚本:在while循环完成之前显示进度指示器
【发布时间】:2016-06-20 04:16:32
【问题描述】:

我已经能够在某些进程运行直到它们完成时显示进度指示器,但是当我运行 while 循环时,我无法让代码正确显示相同的进度指示器。例如,当我在脚本中执行 ping 操作时,我会这样做:

/bin/ping -c 10 $ipaddress > /pinglog & PID=$!
printf "["
while kill -0 $PID 2> /dev/null; do
    printf "."
    sleep 1
done
printf "] "

我还可以在脚本中使用 sleep 命令显示进度:

sleep 30 & PID=$!
printf "["
while kill -0 $PID 2> /dev/null; do
 printf  "."
 sleep 1
done
printf "] "

这两个示例都有效,我的输出如下所示:

[.........................}

这些点每秒都在继续,直到命令完成,这就是我想要的。但是我现在正在尝试使用 while 循环来获得相同的输出,但我无法获得我想要的结果。这是我到目前为止所拥有的。

while [[ ! $(/bin/grep -wo -m1 $VAR1 /logfile) ]] & PID=$!
printf "["
while kill -0 $PID 2> /dev/null; do
    printf "."
    sleep 1
done
printf "] "
do
sleep 5
done
echo $VAR1 was found

这会运行,但是当找到字符串并且输出如下所示时它不会停止:

[.] [.] [.] [.] [.] and on and on...

我尝试移动实际的 while 循环 do, sleep 5 并在第一个 printf 语句上方完成,但结果更糟,我什至没有得到进度指示器输出。如何解决此问题,以便获得与前 2 个示例相同的进度指示器输出,并且在找到我的模式时停止?

【问题讨论】:

    标签: bash


    【解决方案1】:

    while 测试的命令是do 之前的最后一个命令。所以你的循环基本上是while ...; printf "] "; do ... ; end,因为printf永远不会失败,while永远不会终止。

    不过,我不清楚您希望使用while 循环测试什么。如果期望复合命令 [[ ! $(/bin/grep -wo -m1 $VAR1 /logfile) ]] & 将被测试,您应该注意,当您在后台运行命令时,状态码仅反映 bash 是否能够 fork 一个子以​​便运行后台命令(几乎总是如此)。命令的状态码在命令完成之前是不可用的,所以如果你只想检查状态码,在后台运行它或多或少是没有意义的。

    如果您希望这样做,您可以将整个 while 循环作为背景;将& 放在终止while 复合语句的done 之后。这可能就是您真正想要的:

    printf "["
    while ! grep -wqm1 "$VAR1" logfile; do
      sleep 1
    done &
    while kill -0 $! 2>/dev/null; do
      printf "."
    done
    printf "] "
    

    注意:grep-q 选项会导致它只产生一个状态返回。 (通过这种更改,-m1 实际上是多余的。)这避免了必须使用[[ 来测试输出。除非您 100% 确定 $VAR1 不能包含空格或 shell 元字符,否则您应该用引号将扩展括起来。无需将$! 存储在变量中,因为它将继续包含相同的值,直到您使用其他命令。

    【讨论】:

    • 我正在使用 while 循环测试某个日志文件中是否存在模式。当我等待模式出现时,我想显示一个进度指示器,就像我在前两个示例中介绍的那样,使用另一个 while 循环。所以可以说是嵌套循环。当模式出现时,printf [......] 应该停止。
    • 只是复习你说的话。但是,printf 将/应该失败/完成/无论如何......当初始 while 循环的 PID 终止时(当找到模式时)。但事实并非如此。至少没有这段代码。
    • @user53029: printf 不会失败。为什么要这样做?
    • 现在可以了。我已经重新编码了。请参阅我发布的答案。您的回答让我思考并进行了更多测试,所以谢谢你。
    • @user53029:您现在在后台运行sleep 5,恕我直言,这毫无意义。你也可以写for ((i = 0;i < 5;++i)); do printf "."; sleep 1; done 并省略整个while kill -0 循环。你真正想做的是printf "["; while ! grep -wq "$VAR1" logfile; do sleep 1; done & while kill -0 $! 2>/dev/null; do printf "."; done; printf "] "
    【解决方案2】:

    我已经解决了这个问题。要获得 [........] 输出格式,您必须在第一个 while 循环开始之前启动第一个 printf "[" 。然后将 sleep 5 命令置于后台,并在其 PID 上使用第二个 while 循环,而不是初始 while 循环本身,因为我们要终止 sleep 5 命令而不是 while 循环本身。这将允许点在括号内每秒而不是每 5 秒重复一次,并允许找到搜索字符串。最后将最后一个 printf "]" 放在最后一个 done 之外,以在搜索字符串的 while 循环结束后完成格式化。这可以防止每五秒重复一次 [.]。这对我有用:

    printf "["
    while [[ ! $(/bin/grep -wo -m1 $VAR1 /logfile) ]]
    do
    sleep 5 & PID=$!
    while kill -0 $PID 2> /dev/null; do
        printf "."
        sleep 1
    done
    done
    printf "] "
    echo $VAR1 was found
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-09-03
      • 1970-01-01
      • 2020-09-04
      • 2019-04-09
      • 1970-01-01
      • 1970-01-01
      • 2012-09-10
      相关资源
      最近更新 更多