如果您安装了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)并开始处理文件。
xargs 的 -n50 参数使其仅等待前 50 个文件,以避免在 find 花费大量时间未找到第一个文件时延迟启动;可能没有必要。
awk 的 -vRS='\0' 参数使其使用 NUL 作为行分隔符,以匹配 -print0 操作到 find(以及 -0 选项到 xargs);仅当文件路径可以包含换行符时,所有这些都是必需的。
awk 将计数写入_total,然后我们将_total 符号链接到total,以避免在完全写入之前读取total 的非常不可能的竞争条件。符号链接是原子的,所以这样做可以保证total 要么不存在,要么完全写入。
计算文件的总大小可能比只计算文件大小更好,尤其是在处理工作与文件大小相关的情况下(例如压缩)。那将是一个相当简单的修改。此外,使用xargs 并行执行功能也很诱人,但这需要更多的工作来协调并行进程之间已处理文件的总和。
如果您使用的是没有dialog 的托管环境,最简单的解决方案是在有dialog 的环境中使用ssh 运行上述脚本。从 run.sh 中删除 | dialog --gauge "Starting..." 7 70,并将其放入您的 ssh 调用中:ssh user@host /path/to/run.sh root-dir find-tests | dialog --gauge "Starting..." 7 70