在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

浙公网安备 33010602011771号