1

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

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

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

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

有没有办法让某种类型的进度条、完成百分比,甚至打印周期(即,工作......)直到完成?如果可能的话,我想通过添加另一个find. 可能吗?

提前致谢。

4

3 回答 3

1

显然,如果您知道命令将花费多长时间运行,或者如果它可以告诉您它已经完成了 y 个任务中的 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 "{}" \;
于 2013-06-28T20:25:59.077 回答
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扩展。我没有列出完整的清单,但它肯定包括%qprintf 格式(可能只是%s);用于 NUL 终止文件名列表的--tmpdir标志,以及mktemp.

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

-n50参数使其xargs仅等待前 50 个文件,以避免在 find 花费大量时间未找到第一个文件时延迟启动;可能没有必要。

to的-vRS='\0'参数awk使其使用 aNUL作为行分隔符,以匹配-print0操作 to find(和-0选项 to xargs);仅当文件路径可以包含换行符时,所有这些都是必需的。

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

计算文件的总大小可能比只计算文件更好,特别是如果处理工作与文件大小相关(例如压缩)。那将是一个相当简单的修改。此外,使用并行执行功能很诱人xargs,但这需要更多的工作来协调并行进程之间处理的文件的总和。

如果您使用的dialogssh没有dialog. 从 run.sh 中删除| dialog --gauge "Starting..." 7 70,并将其放入您的ssh调用中:ssh user@host /path/to/run.sh root-dir find-tests | dialog --gauge "Starting..." 7 70

于 2013-06-29T06:51:34.393 回答
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,所以不会干扰。)

于 2013-06-30T18:58:46.277 回答