命令行进度条完全指南:倒计时、缓冲区刷新与动态下载

进度条---命令行版本

前置知识

1.回车换行

  • \r回车,将光标移动到当前行的开头。
  • \n换行,将光标移动到下一行的相同位置(通常是下一行开头)。

在终端中,单独使用 \r 可以实现在同一行覆盖输出,常用于进度条或倒计时。

2.缓冲区问题

在这个程序中,如果不带\n,会发现,先休眠3s,再输出hello bit ,但我们知道程序都是从上到下执行的,那在这3s,printf去哪里了呢?答案是缓冲区。显示器都是按行刷新的,不带\n无法刷新,最后程序结束。会自动刷新缓冲区,从而输出结果。

在Linux中,一切皆文件。

在 Linux 中,一切皆文件:

  • stdin → 键盘
  • stdout → 显示器
  • stderr → 显示器(错误输出)

那怎么直接刷新输出结果呢?

用fflush

3.理解显示

关键点:显示器只认字符!任何图形、进度条等最终都要转化为字符序列输出。

4.简单倒计时程序

c 复制代码
  1 #include<stdio.h>
  2 #include<unistd.h>
  3 int main()
  4 {
  5     int n=10;
  6     while(n>=0)
  7     {
  8         printf("%-2d\r",n);//\r是为了回车,让光标回到其实位置。-是向左对其。
  9         fflush(stdout);
 10         sleep(1);
 11         n--;
 12     }
 13
 14    return 0;
 15 }

该程序目的是实现原地倒计时,效果如下

进度条

版本一:基础进度条

1.创建相关目录,及其文件

创建processbar目录,创建.h .c文件 还有main.c文件 以及makefile

Makefile内容

makefile 复制代码
 1 SRC=$(wildcard *.c)
  2 OBJ=$(SRC:.c=.o)
  3 BIN=processbar
  4 
  5 $(BIN):$(OBJ)
  6     gcc -o $@ $^
  7 %.o:%.c
  8     gcc -c $<
  9 .PHONY:
 10 clean:
 11     rm -f $(OBJ) $(BIN)
                              
2.process.c
c 复制代码
  1 #include"process.h"
  2 #include<unistd.h>
  3 #include<string.h>
  4 #define NUM 101
  5 #define STYLE '='
  6 void process_v1()
  7 {
  8     char buffer[NUM];
  9     memset(buffer,0,sizeof(buffer));
 10     const char *lable="|/-\\";
 11     int len=strlen(lable);
 12     int cnt=0;
 13     while(cnt<=100)
 14     {
 15         printf("[%s][%%%d][%c]\r",buffer,cnt,lable[cnt%len]);
 16         fflush(stdout);
 17         cnt[buffer]=STYLE;
 18         cnt++;
 19         usleep(50000);
 20     }
 21     printf("\n");
 22 }
 23 
3.process.h
c 复制代码
 1 #pragma once
  2 
  3 #include<stdio.h>
  4 
  5 void process_v1();
4.main.c
c 复制代码
  1 #include"process.h"
  2 int main()
  3 {
  4     process_v1();
  5     return 0;
  6 }
       
演示效果

这个版本只用于演示进度条的基本原理,在实际开发中无法与下载、拷贝等任务结合使用。请看下一版本。

版本二:结合下载场景的动态进度条

这个版本将进度条与一个模拟的下载任务结合,每次下载一点数据就刷新一次进度条。

main.c
c 复制代码
 1 #include"process.h"
  2 #include<stdio.h>
  3 
  4 double total =1024.0;
  5 double speed =1.0;
  6 
  7 void DownLoad()
  8 {
  9     double current = 0;
 10     while(current <= total)
 11     {
 12         FlushProcess(total,current);
 13         //下载代码
 14         usleep(10000);//充当下载数据
 15         current += speed;
 16     }
 17     printf("\ndownload %.21fMB Done\n",total);
 18 }
 19 
 20 int main()
 21 {
 22     DownLoad();
 23 //  process_v1();
 24     return 0;
 25 }
process.c
c 复制代码
  1 #include"process.h"
  2 #include<unistd.h>
  3 #include<string.h>
  4 #define NUM 101
  5 #define STYLE '='
  6 void FlushProcess(double total,double current)
  7 {
  8 
  9     char buffer[NUM];
 10     memset(buffer,0,sizeof(buffer));
 11     const char *lable="|/-\\";
 12     int len=strlen(lable);
 13 
 14     static int cnt=0;
 15     //不需要自己循环,填充#
 16     int num=(int)(current*100/total);
 17     for(int i=0;i<num;i++)
 18     {
 19         buffer[i]=STYLE;
 20     }
 21     double rate =current/total;
 22     cnt %=len;
 23     printf("[%-100s][%1.f%%][%c]\r",buffer,rate*100,lable[cnt]);
 24     cnt++;
 25     fflush(stdout);
 26 }
 27 void process_v1()
 28 {
 29     char buffer[NUM];
 30     memset(buffer,0,sizeof(buffer));
 31     const char *lable="|/-\\";
 32     int len=strlen(lable);
 33     int cnt=0;
 34     while(cnt<=100)
 35     {
 36         printf("[%s][%%%d][%c]\r",buffer,cnt,lable[cnt%len]);
 37         fflush(stdout);
 38         cnt[buffer]=STYLE;
 39         cnt++;
 40         usleep(50000);
 41     }
 42     printf("\n");
 43 }
 44 
process.h
c 复制代码
  1 #pragma once
  2 
  3 #include<stdio.h>
  4 
  5 void FlushProcess(double total,double current);
  6 void DownLoad();
  7 void process_v1();
                   
Makefile
makefile 复制代码
 1 SRC=$(wildcard *.c)
  2 OBJ=$(SRC:.c=.o)
  3 BIN=processbar
  4 
  5 $(BIN):$(OBJ)
  6     gcc -o $@ $^
  7 %.o:%.c
  8     gcc -c $< -std=c99
  9 .PHONY:
 10 clean:
 11     rm -f $(OBJ) $(BIN)
最终结果演示

为了降低代码耦合度,也可以这样写

main.c

c 复制代码
  1 #include"process.h"
  2 #include<stdio.h>
  3 
  4 double total =1024.0;
  5 double speed =1.0;
  6 
  7 typedef void (*callback_t)(double total,double current);//定义回调函数
  8 
  9 void DownLoad(callback_t cb)
 10 {
 11     double current = 0;
 12     while(current <= total)
 13     {
 14         cb(total,current);
 15         //下载代码
 16         usleep(10000);//充当下载数据
 17         current += speed;
 18     }
 19     printf("\ndownload %.21fMB Done\n",total);
 20 }
 21 
 22 int main()
 23 {
 24     DownLoad(FlushProcess);
 25 //  process_v1();
 26     return 0;
 27 }
相关推荐
薛定猫AI18 分钟前
Codex 与 Claude Code 全平台安装配置指南(Windows / macOS / Linux)
linux·windows·macos
kidwjb8 小时前
信号量在进程中的使用
linux·进程间通信
sulikey10 小时前
个人Linux操作系统学习笔记2 - gcc与库的理解
linux·笔记·学习·操作系统·gcc·
二宝哥10 小时前
Linux虚拟机网络配置
linux·运维·服务器
陳103010 小时前
Linux:进程间通信 和 简单进程池
linux·运维·服务器
jimy111 小时前
改.bashrc,直观地判断本地repo是否有改动
linux·服务器
愚昧之山绝望之谷开悟之坡11 小时前
什么是Linter?什么是沙箱!
linux·笔记
babytiger12 小时前
Gitea 重安装 + Snap 数据迁移完整流程总结
linux·elasticsearch·gitea
匆匆那年96712 小时前
远程 Linux 校园网认证操作手册(本地浏览器法)
linux·运维·服务器
newnazi12 小时前
RedHat10 安装MS SQL Server2025
linux·服务器·数据库