可以用Unix shell以某种方式报告完成状态吗?

时间:2013-06-28 20:04:16

标签: bash shell scripting ksh

我已经看到围绕SO的进度条和外部特定命令(例如cat)的一些想法。但是,我的问题似乎与标准略有不同......

目前,我在shell中使用find命令的功能,例如以下示例:

find . -name file -exec cmd "{}" \;

其中“cmd”通常是用于释放磁盘空间的压缩功能或删除工具。 什么时候 ”。”非常大,这可能需要几分钟,我希望能够报告“状态”。

有没有办法让某种类型的进度条,完成百分比,甚至印刷期(即工作......)直到完成?如果可能的话,我想通过添加另一个find来避免增加执行的持续时间。有可能吗?

提前致谢。

3 个答案:

答案 0 :(得分:1)

显然,如果你知道命令运行多长时间,或者它可以告诉你它完成了x任务,你只能有一个进度表或完成百分比。

这是一种在某些工作正常时显示指标的简单方法:

#!/bin/sh
echo "launching: $@"
spinner() {
    while true; do
        for char in \| / - \\; do
            printf "\r%s" "$char"
            sleep 1
        done
    done
}
# start the spinner
spinner &
spinner_pid=$!
# launch the command
"$@"
# shut off the spinner
kill $spinner_pid
echo ""

所以,你要做(假设脚本名为“progress_indicator”)

find . -name file -exec progress_indicator cmd "{}" \;

答案 1 :(得分:0)

如果您安装了dialog实用程序(),则可以轻松地进行滚动显示:

find . -type f -name glob -exec echo {} \; -exec cmd {} \; |
dialog --progressbox "Files being processed..." 12 $((COLUMNS*3/2))

--progressbox的参数是框的标题(可选,看起来不像数字);文本行中的高度和文本列中的宽度。 dialog有许多选项来自定义演示文稿;以上只是为了让你开始。

dialog也有一个进度条,也称为“衡量标准”,但正如@glennjackman在答案中指出的那样,你需要知道要做多少工作才能显示进度。一种方法是收集find命令的整个输出,计算其中的文件数,然后从累积的输出中运行所需的任务。但是,这意味着要等到find命令完成才能开始工作,这可能是不可取的。

仅仅因为这是一个有趣的挑战,我提出了以下解决方案,它可能过度设计,因为它试图解决我能想到的所有shell陷阱(即便如此,它可能会错过一些)。它由两个shell文件组成:

# File: run.sh

#!/bin/bash
# Usage: run.sh root-directory find-tests
#
# Fix the following path as required
PROCESS="$HOME/bin/process.sh"
TD=$(mktemp --tmpdir -d gauge.XXXXXXXX)
find "$@" -print0 |
tee >(awk -vRS='\0' 'END{print NR > "'"$TD/_total"'"}';
      ln -s "$TD/_total" "$TD/total") |
{ xargs -0 -n50 "$PROCESS" "$TD"; printf "XXX\n100\nDone\nXXX\n"; } |
dialog --gauge "Starting..." 7 70 
rm -fR "$TD"

# File: process.sh

#!/bin/bash
TD="$1"; shift
TOTAL= 
if [[ -f $TD/count ]]; then COUNT=$(cat "$TD/count"); else COUNT=0; fi
for file in "$@"; do
  if [[ -z $TOTAL && -f $TD/total ]]; then TOTAL=$(cat "$TD/total"); fi
  printf "XXX\n%d\nProcessing file\n%q\nXXX\n" \
         $((COUNT*100/${TOTAL:-100})) "$file"
  #
  # do whatever you want to do with $file
  #
  ((++COUNT))
done
echo $COUNT > "$TD/count"

一些注意事项:

上面分散了很多gnu扩展。我还没有完整列表,但肯定包含%q printf格式(可能只是%s);用于NUL的标志终止文件名列表,--tmpdir标志用于mktemp

run.sh使用tee同时计算找到的文件数(使用awk)并开始处理文件。

-n50的{​​{1}}参数导致它仅等待前50个文件,以避免在查找花费大量时间未找到第一个文件时延迟启动;可能没有必要。

xargs的{​​{1}}参数会导致它使用-vRS='\0'作为行分隔符,以将awk操作与NUL匹配(以及{ {1}} -print0的选项;只有在文件路径可以包含换行符时才需要这一切。

find将计数写入-0,然后我们将xargs符号链接到awk,以避免在_total之前读取_total的真正不可能的竞争条件完全写的。符号链接是原子的,所以这样做可以保证total不存在或完全写入。

计算文件的总大小而不是仅计算它们可能更好,尤其是在处理工作与文件大小(例如,压缩)相关时。这将是一个相当简单的修改。此外,使用total并行执行功能很有诱惑力,但这需要更多的工作来协调并行进程之间处理文件的总和。

如果您使用的是没有total的托管环境,最简单的解决方案是使用xargs从具有dialog的环境运行上述脚本。从run.sh中删除ssh,然后将其放入dialog调用中:| dialog --gauge "Starting..." 7 70

答案 2 :(得分:0)

find的技巧是你添加两个-print子句,一个在开头,然后 一个在最后。然后使用awk(或perl)更新并打印每个行计数器 独特的路线。在这个例子中,我告诉awk打印到stderr。

任何重复的行必须是我们指定条件的结果,因此我们将其视为特殊行。 在这个例子中,我们只打印该行:

find . -print -name aa\* -print |
awk '$0 == last {
    print "" > "/dev/fd/2"
    print
    next
}
{
    printf "\r%d", n++ > "/dev/fd/2"
    last=$0
}'

最好让find只报告路径名,并从awk做进一步处理, 或者只是添加另一个管道。 (因为计数器打印到stderr,那些不会 干扰。)