2013-06-28 32 views
1

我已经看到了一些围绕SO的进度条和外部特定命令(如cat)的想法。不过,我的问题似乎从标准稍微偏离...可以使用Unix shell以某种方式报告完成状态吗?

目前,我正在使用外壳find命令的能力,如遵循例如:

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

其中“CMD”是通常是一个压缩功能或删除工具来释放磁盘空间。 当“。”是非常大的,这可能需要几分钟,我希望能够报告“状态”。

有什么方法可以让某种类型的进度条,完成百分比,甚至打印期间(即工作....)等待完成?如果可能的话,我想通过增加另一个find来避免增加这个执行的持续时间。可能吗?

在此先感谢。

回答

1

显然,如果您知道命令运行需要多长时间,或者它可以告诉您它已完成x个y中的任务,则只能有一个进度表或百分比完成。

这里有一个简单的方法来显示一个指标,而一些工作:

#!/bin/sh 
echo "launching: [email protected]" 
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 
"[email protected]" 
# shut off the spinner 
kill $spinner_pid 
echo "" 

所以,你会做(假定脚本名为“progress_indicator”)

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

对,你有没有想法如何确定数字任务,“Y”,没有显着增加运行时?不幸的是,'code'find'code'需要很长的时间才能枚举文件。'du'命令可能会起作用吗?我会在更多的研究后更新。现在,这对我来说似乎是一个很好的解决方法。我会让你知道它是怎么回事。 – Bts

+0

这似乎是打印每个文件进行操作的进度指示器。无论如何要把它扩展到整个查找命令?换句话说,可以通过某种方式更改此代码,以便不会为找到的每个文件都打印新的微调器,而是为了查找的整个操作? – Bts

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命令完成才能开始工作,这可能不合意。

仅仅因为这是一个有趣的挑战,我提出了以下解决方案,这可能是过度设计的,因为它试图解决所有我能想到的外壳问题(甚至如此,它可能会错过一些) 。它由两个壳文件:

# 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 "[email protected]" -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 "[email protected]"; 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终止文件名列表的标志和mktemp--tmpdir标志。

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

-n50参数xargs导致它只会等待前50个文件,以避免在找到第一个文件时花费大量时间来延迟启动;它可能没有必要。

-vRS='\0'参数awk导致它使用一个NUL作为行定界符,以匹配-print0行动find(以及-0选项xargs);所有这些仅在文件路径可能包含新行时才是必需的。

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

对文件的总大小进行计数而不是对其进行计数可能会更好,尤其是在处理工作与文件大小(例如压缩)相关的情况下。这将是一个相当简单的修改。此外,使用并行执行功能也很诱人,但这需要更多的工作来协调并行进程之间处理文件的总和。

如果您使用的托管环境没有dialog,最简单的解决方案是仅使用ssh从具有dialog的环境运行上述脚本。从run.sh删除| dialog --gauge "Starting..." 7 70,并把它放在你的ssh调用来代替:ssh [email protected] /path/to/run.sh root-dir find-tests | dialog --gauge "Starting..." 7 70

+0

你的答案如何改变文件的总大小?如果总大小为GB,会减慢执行速度吗?这正是我所期待的,但似乎我的外部管理环境不包含'对话'命令。我将不得不提出另一种解决方案。 – Bts

+0

要使第二种解决方案适用于文件大小,您需要用'-printf“%s /%p \ 0”'替换'-print0'(它将打印大小以及名称,用'/'隔开,仍然以'NUL'结束,然后'awk'调用需要像'awk -vFS =/-vRS ='\'''{s + = $ 1} END {print s }“'在处理循环中,您需要通过添加'filename = $ {file#。* /}'来删除大小,您还必须想出一个合理的开始总大小值。编辑关于'dialog'的回答 – rici

+0

糟糕,'filename =“$ {file#* /}”' – rici

0

与找到诀窍是增加了两个条款-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 
}' 

这是最好的离开找到只报告路径名,并从AWK做进一步的处理, 或添加另一管道。 (因为计数器打印到stderr,那些将不会 干扰。)

相关问题