Linux 进度条实现原理

进度条的的动态增长是利用人的视觉短暂停留效果的,不断从输出缓冲区刷新出相同的内容,在肉眼看来进度条在不断的增长。

缓冲区

linux下的每一个进程会维护一个print/scanf的缓冲区,缓冲区有一个概念叫做缓冲方式,就是说达到一定的方式,缓冲区的内容才会被刷新。

标准C库函数常见的三种缓冲方式:

  1. 行缓冲:遇到”\n”,就会刷新缓冲区
  2. 全缓冲:把缓冲区写满,再进行刷新缓冲区
  3. 无缓冲:系统调用函数无缓冲区(如:write)

默认情况下,一般采用行缓冲模式
当程序退出时缓冲区自动刷新,所以,为了每次打印每行的内容,需要利用fflush()函数来进行强制刷新缓冲区。

带颜色的printf/fprintf打印

终端的字符颜色由转义序列(Escape Sequence)控制,是文本模式下的系统显示功能,与具体语言无关

转义序列以控制字符’ESC’开头,该字符的ASCII码十进制表示为27,十六进制表示为0x1B,八进制表示为033。多数转义序列超过两个字符,故通常以’ESC’和左括号’[‘开头。该起始序列称为控制序列引导符(CSI,Control Sequence Intro),通常由’\033[‘或’\e[‘代替。

通过转义序列设置终端显示属性时,可采用以下格式:

1
2
3
\033[ Param {;Param;...}m

\e[ Param {;Param;...}m

‘\033[‘或’\e[‘引导转义序列,’m’表示设置属性并结束转义序列。

Param为属性值,{…}表示可选(多个参数之间用分号隔开,与顺序无关)。

例如:

1
# echo -e "\e[31;47m test colour . \e[0m"

即设置输出为红色字体(31),白色背景(47)。选项’-e’为echo命令激活特殊字符的解析器

注意,转义序列可被控制字符’CAN’(Cancel )和’SUB’(Substitute)中断。

转义序列相关的常用参数如下(通过man console_codes命令可查看更多的参数描述):

类别颜色
显示方式0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、5(闪烁)、25(非闪烁)、7(反显)、27(非反显)
前景色30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋红)、36(青色)、37(白色)
背景色40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋红)、46(青色)、47(白色)

颜色:0(黑)、1(红)、2(绿)、 3(黄)、4(蓝)、5(洋红)、6(青)、7(白)

前景色为30+颜色值,如31表示前景色为红色;背景色为40+颜色值,如41表示背景色为红色。

因此,通过转义序列设置终端显示属性时,常见格式为:

1
2
3
\033[显示方式;前景色;背景色m输出字符串\033[0m

\e[显示方式;前景色;背景色m输出字符串\033[0m

其中 ,’\033[0m’用于恢复默认的终端输出属性,否则会影响后续的输出。

实现方式

C语言实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
char buf[102]={0};//101个数字+'\0'
char *str="-\|/";
int i=0;
for(;i<=100;++i)
{
printf("\033[034m[%-100s],[%d%%],[%c]\033[0m\r",buf,i,str[i%4]);

fflush(stdout);
usleep(20000);
buf[i]='#';
}
printf("\n");
return 0;
}

SHELLE实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

i=0
b=""
array=('-' '\\' '|' '/')

while [ $i -le 100 ]
do
let idx=i%4
printf "\e[31m\033[40m[%-100s]\e[32m\033[47m [%d%%] \e[30m \033[47m [%c] \e[0m\r" "$b" "$i" "${array[$idx]}"
b+='#'
usleep 200000
let i++
done

参考

通过printf设置Linux终端输出的颜色和显示方式

用shell脚本实现彩色进度条