在Bash中显示长时间运行的应用程序的进度

时间:2017-09-22 05:36:21

标签: linux bash installation progress

我有一些涉及嵌入式系统更新的程序,需要按顺序运行,我需要将进度报告给单独的系统。

阶段是:

  • 验证图像;
  • 将映像文件解压缩到活动磁盘上的文件中;和
  • 将图像安装到备用磁盘。

之后,机器重新启动,备用磁盘成为活动磁盘。

目前,我们报告最初为0%,验证结束时为5%,拆包结束时为45%,安装结束时为90%。新图像开始运行后,重新启动后会报告100%标记。

现在,虽然我对这种方法非常满意,但客户希望看到更精细的更新状态报告。我们毫不含糊地告诉他们,任何此类报告都是人为的,但他们认为保持客户满意是必要的。

因此,我正在寻找一种方法来报告基于粗略时间的进度,同时仍然执行长期任务并开始工作。

我们知道从开始到重新启动整个安装过程大约需要两分钟,每个阶段花费的时间按照给定的百分比进行分配。

1 个答案:

答案 0 :(得分:3)

bash中执行此操作的一种方法是运行单独的流程来执行进度报告,并为其提供足够的信息以便成功报告。这将涉及:

  • 起始百分比;
  • 结束百分比;
  • 将delta添加到每个周期的百分比;和
  • 周期时间。

前两个是显而易见的,它们提供了报告内容的下限和上限。

另外两个允许您提供报告比率。例如,每秒增加6%将使用值(delta = 6, cycle = 1),并且每秒增加6%。

对于较慢的步骤,例如每秒0.25%,您将提供(delta = 1, cycle = 4),并且每4秒将添加1%。你实际上并没有报告亚秒级的进展,只是随着时间的推移它会平均化。

那么,这是怎么做到的?首先,让我们提供报告进度的简单功能。在你的情况下,这应该被用来与那个"单独的系统"你提到以下代码显示了如何执行此操作:

#!/usr/bin/env bash

doProgress() {
    # Simply echo progress using whatever was passed. Date is included
    # for debugging purposes.

    echo "$(date +%H:%M:%S): $1%"
}

"肉"解决方案是backgroundReporter()函数,它将根据前面提到的四个参数调用doProgress()

backgroundReporter() {
    # Get the four parameters.

    currVal=$1 ; maxVal=$2 ; deltaVal=$3 ; timeGap=$4

    # If signalled to stop, output maximum percentage then exit.
    # Note no output if you've already reached the maximum.

    trap "[[ \${maxVal} -ne \${lastVal} ]] && doProg \${maxVal} ; exit" HUP

    # Infinite loop until signalled.

    while : ; do
        # Wait for the duration, save current percentage, and
        # calculate new percntage (capped at maximum).

        sleep ${timeGap}
        lastVal=${currVal}
        (( currVal = (currVal + deltaVal > maxVal) ? maxVal : currVal + deltaVal ))

        # Log only if it's changed.

        [[ ${currVal} -ne ${lastVal} ]] && doProg ${currVal}
    done
}

唯一棘手的是信号处理。将此功能作为后台任务运行的调用者负责在阶段完成后向其发送信号。

当然,还有一些基本的测试用例可以证明这一点。首先,我们需要确保在退出时杀死运行backgroundReporter()的子进程。执行此操作的命令将是:(一个简单的无操作),当它 运行时它将运行时为kill -HUP <pid>

killCmd=":" ; trap "${killCmd}" EXIT

然后我们报告零的初始进度并开始三个阶段。它们比指定的问题更短,因为这仅用于演示目的:

doProg 0

# 0 -> 10% at 4%/sec, phase takes six seconds.
bgReport 0 10 4 1 & killCmd="kill -HUP $!" ; sleep 6 ; ${killCmd} ; killCmd=":" ; wait

# 10 -> 20% at 1%/2secs, phase takes six seconds.
bgReport 10 20 1 2 & killCmd="kill -HUP $!" ; sleep 6 ; ${killCmd} ; killCmd=":" ; wait

# 20 -> 100% at 30%/sec, phase takes five seconds.
bgReport 20 100 30 1 & killCmd="kill -HUP $!" ; sleep 5 ; ${killCmd} ; killCmd=":" ; wait

而且,正如您从样本运行中看到的那样,这三个阶段的效果与您预期的一样(右边的评论是我的):

13:15:29: 0%
13:15:30: 4%      4% per sec
13:15:31: 8%
13:15:32: 10%     capped at 10% for remainder of 6-sec slot

13:15:37: 11%     goes up 1% per 2 secs
13:15:39: 12%
13:15:41: 20%     phase finishes, jumps immediately to max

13:15:42: 50%     goes up 30% per second
13:15:43: 80%
13:15:44: 100%    but capped at 100%

对于给出的实际次,两分钟的阶段是5%结束验证,45%结束解包和90%结束安装,以下更合适(阶段稍作修改,所以比率更容易):

doProg 0

# Validation 0 -> 5%, 5 steps in 6 seconds (~ 1%/1s).
bgReport 0 5 1 1 & killCmd="kill -HUP $!"
validate
${killCmd} ; killCmd=":" ; wait

# Unpacking 5 -> 45%, 40 steps in 50 seconds (4%/5s).
bgReport 5 45 4 5 & killCmd="kill -HUP $!"
unpack
${killCmd} ; killCmd=":" ; wait

# Installing 45 -> 95%, 50 steps in 64 seconds (~ 5%/6s)
bgReport 45 95 5 6 & killCmd="kill -HUP $!"
install
${killCmd} ; killCmd=":" ; wait

使用这些比率,您可以保证每隔(最多)6秒获得一次进度更新,这与原始方法不同,在原始方法中它会在45%的时间内保持良好的分钟,然后立即跳到95%。