在shell界面实现解压文件的进度条

在制作一个包含长时间阻塞的命令(比如说解压文件)的shell脚本时,时常会想,如果类似于wget命令能显示一个进度条来提示就好,经过一段时间的摸索,博主总结出了两种方法来实现这个功能:纯shell方法和借助tqdm工具。

先说下结论:

  • 纯shell方法额外资源开销非常大,但因为不借助第三方工具,可移植性强
  • tqdm资源开销少,界面美观,但需要有python环境

下面以解压文件为例分别介绍下两种方法:

纯shell方法

参考这篇博客:

https://www.jianshu.com/p/cf018f94224b

博客本身已经讲解的十分清楚了,不过他是借助了whiptail工具显示的进度条,我们想类似于wget在命令行显示进度条可以将whiptail --gauge "Extracting $FILE..." 6 60 0替换为自己显示进度条的脚本,我先抛砖引玉下:

#!/bin/bash
#/root/progress.sh

null=
while read -u 0 stdinput
do
        residue=$((100-stdinput))
        printf "progress:[%0${stdinput}d%${residue}s]%d%%\r" "${null}" "${null}" "${stdinput}"
done

修改后的脚本:

#!/bin/bash
#Tarprogressbar.sh
#一个用来显示tar解压缩某文件时进度条的脚本

FILE=$1

TOTAL_SIZE=0
for FILE_SIZE in $(tar tvvf $FILE | awk '{print $3}'); do
    if [ "$FILE_SIZE" = "${FILE_SIZE//[^0-9]/}" ]; then
        TOTAL_SIZE=$((TOTAL_SIZE+FILE_SIZE))
    fi
done

TMPFIFO=/tmp/tmpfifo &> /dev/null
if [[  -f $TMPFIFO ]];then
:
else
    mkfifo $TMPFIFO &> /dev/null
fi

(
TOTAL_FILE_SIZE_UNZIP=0
{
p=1
while read line
do
    FILE_SIZE_UNZIP=$(echo $line | awk '{print $3}')
    ((TOTAL_FILE_SIZE_UNZIP=$TOTAL_FILE_SIZE_UNZIP+$FILE_SIZE_UNZIP))
    echo $((TOTAL_FILE_SIZE_UNZIP*100/TOTAL_SIZE))
done<$TMPFIFO
rm -rf $TMPFIFO
echo 100
} | /root/progress.sh
) &
B_PID=$!

tar zxvvf  $FILE  >$TMPFIFO 2>/dev/null
wait $B_PID
echo " unzip ended successfully. "

执行结果:

root@localhost:/tmp# /root/Tarprogressbar.sh  etc.tgz
progress:[000000000000000000000000000000000000000000000000000000000000                                        ]60%

tqdm工具

tqdm是python的一个库,详见

https://pypi.org/project/tqdm/#description

下载方式:

pip install tqdm

没有pip的也可以用apt:

apt install python3-tqdm

安装好tqdm后就可以使用tqdm命令了,tqdm在管道之间时使用时会将所有的stdin传递到stdout,同时将统计得出的进度打印到stderr

默认情况下tqdm统计的是输入了多少行,--total=<total>参数指定了预期统计总数,看不懂?没关系,上一个官方示例估计大家就理解了:

$ time find . -name '*.py' -type f -exec cat \{} \; | wc -l
857365
$ find . -name '*.py' -type f -exec cat \{} \; |
    tqdm --unit loc --unit_scale --total 857366 >> /dev/null
100%|█████████████████████████████████| 857K/857K [00:04<00:00, 246Kloc/s]

但是我们在解压时需要统计的是stdin的字节数,怎么办呢?这时候需要添加个--bytes参数就可以了:

root@bling5g-Lenovo:/tmp# cat etc.tgz |tqdm --bytes --total `ls -l etc.tgz|cut -d" " -f5`|tar zxf -
100%|█████████████████████████████████| 2.66M/2.66M [00:00<00:00, 25.6MB/s]
root@bling5g-Lenovo:/tmp#

压缩时怎么办呢?以将/etc压缩成etc.tgz为例,一开始我是想通过将/etc目录的大小近似等于etc.tar的大小来执行,没想到etc.tar是大于/etc的,这就导致了执行完后就不显示ETA和进度条了(压缩过程中还是有的):

root@bling5g-Lenovo:/tmp# tar -cf - /etc/|tqdm --bytes --total `du -sb /etc | cut -f1`|gzip > etc.tgz
tar: 从成员名中删除开头的“/”
11.0MB [00:00, 29.7MB/s]

如果想要在最后保留进度条的话可以这样:

root@bling5g-Lenovo:/tmp# tar -cf etc.tar /etc/;cat etc.tar|tqdm --bytes --total `ls -l etc.tar|cut -d" " -f5`|gzip > etc.tgz
tar: 从成员名中删除开头的“/”
100%|█████████████████████████████████| 11.0M/11.0M [00:00<00:00, 29.8MB/s]
root@bling5g-Lenovo:/tmp#

介绍完了,你更喜欢哪种方式呢

0518be40-ee72-4e96-9848-69e5580ab67b

posted @ 2023-11-05 15:53  江子无怒  阅读(0)  评论(0)    收藏  举报  来源