
🔥草莓熊Lotso: 个人主页
❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》
✨生活是默默的坚持,毅力是永久的享受!
🎬 博主简介:

文章目录
- 前言:
- [一. 核心预备知识:掌握三大关键点,轻松避开进度条开发常见误区](#一. 核心预备知识:掌握三大关键点,轻松避开进度条开发常见误区)
-
- [1.1 回车(\r)与换行(\n)的本质区别解析](#1.1 回车(\r)与换行(\n)的本质区别解析)
- [1.2 深入理解行缓冲区运行机制](#1.2 深入理解行缓冲区运行机制)
- [1.3 进度条的核心构成元素详解](#1.3 进度条的核心构成元素详解)
- [二. 实战开发:打造动态彩色进度条](#二. 实战开发:打造动态彩色进度条)
-
- [2.1 基础版进度条模拟实现(演示原理,实际应用较少)](#2.1 基础版进度条模拟实现(演示原理,实际应用较少))
- [2.2 自动化构建流程:Makefile编写详解](#2.2 自动化构建流程:Makefile编写详解)
- [2.3 头文件设计(process.h):接口函数声明](#2.3 头文件设计(process.h):接口函数声明)
- [2.4 核心实现文件(process.c):关键逻辑代码剖析](#2.4 核心实现文件(process.c):关键逻辑代码剖析)
- [2.5 主函数模块(main.c):应用场景测试](#2.5 主函数模块(main.c):应用场景测试)
- [三. 操作实践与效果展示](#三. 操作实践与效果展示)
- 结尾:
前言:
在
Linux开发中,进度条是最基础也最实用的系统程序之一 ------ 无论是文件下载、编译构建还是数据处理,一个直观的动态进度条能极大提升用户体验。但看似简单的进度条,背后藏着行缓冲区、回车换行区别、字符刷新等关键知识点。本文从基础概念入手,先拆解进度条的实现逻辑,再逐步实现 "固定速度" 和 "自适应进度" 两个版本,最后优化细节让进度条更流畅美观,帮你不仅 "会写",还能 "理解为什么这么写"。
一. 核心预备知识:掌握三大关键点,轻松避开进度条开发常见误区
在动手写代码前,必须先理清 3 个关键概念,否则容易出现 "进度条不刷新""换行错乱" 等问题。
1.1 回车(\r)与换行(\n)的本质区别解析
- 换行(
\n):光标移动到下一行的行首,但不会回到当前行开头;我们日常使用它的时候其实是回车+换行的作用(=/r/n) - 回车(
\r):光标回到当前行的行首,但不会移动到下一行; - 进度条的核心 是 "在同一行反复覆盖刷新",因此必须用
\r让光标回到行首,再重新打印新的进度信息。
1.2 深入理解行缓冲区运行机制
C 语言的printf函数默认是 "行缓冲"--- 只有遇到\n、缓冲区满或手动刷新(fflush(stdout))时,才会把缓冲区的内容输出到终端。
错误示例:
cpp
#include <stdio.h>
int main()
{
printf("hello Lotso!");
sleep(3);
return 0;
}
- 如果只写printf 而不加\n或fflush,内容会一直存在缓冲区,终端看不到任何输出,这就是很多人写进度条 "没反应" 的原因。
注意 :虽然没显示出来,但是我们的C语言默认是顺序结构的,一定是先执行printf再执行sleep的。
示例修正:
cpp
#include <stdio.h>
int main()
{
printf("hello bite!");
fflush(stdout);
sleep(3);
return 0;
}
练手倒计时小程序:
cpp
#include<stdio.h>
#include<unistd.h>
int main()
{
int cnt = 10;
for(;cnt >= 0;cnt--)
{
printf("倒计时: %-2d\r",cnt); // 注意格式控制
fflush(stdout);
sleep(1);
}
printf("\n");
return 0;
}
1.3 进度条的核心构成元素详解
一个完整的动态进度条通常包含 3 部分:
- 进度条主体: 用=等字符填充,直观显示完成比例;
- 百分比: 显示完成进度(0%~100%);
- 动态光标: 用
|/-\循环切换,提示程序正在运行。 - 附加信息: 如当前进度 / 总进度、传输速度,提升实用性。

二. 实战开发:打造动态彩色进度条
基于基础框架,我们会慢慢优化实现出 "彩色区分 + 速度显示 + 多场景适配" 的进度条,核心分为头文件、实现文件、主函数三部分。
2.1 基础版进度条模拟实现(演示原理,实际应用较少)
这个版本的其他文件我就不写了,就展示一个proces.c,
cpp
#include "process.h"
#include <string.h>
#include <unistd.h>
#define NUM 101
#define STYLE '='
// vesion1
void process_v1() {
char buffer[NUM];
memset(buffer, '\0', sizeof(buffer));
const char* lable = "|/-\\";
int len = strlen(lable);
int cnt = 0;
while (cnt <= 100)
{
printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt % len]);
fflush(stdout);
buffer[cnt] = STYLE;
cnt++;
usleep(50000);
}
printf("\n");
}
2.2 自动化构建流程:Makefile编写详解
为了方便编译和清理,编写 Makefile 实现自动化构建,只需一条命令即可生成可执行文件:我们在之前讲过 Makefile 的编写策略,这里就不多说了直接展示
bash
Bin=process_bar
Cc=gcc
Src=$(wildcard *.c)
Obj=$(Src:.c=.o)
$(Bin):$(Obj)
@echo "$^ link to $@"
@$(Cc) -o $@ $^
%.o:%.c
@echo "compiling $< to $@"
@$(Cc) -c $<
.PHONY:clean
clean:
@echo "File cleanup complete"
@rm -f $(Obj) $(Bin)
.PHONY:debug
debug:
@echo "Bin: $(Bin)"
@echo "Cc: $(Cc)"
@echo "Src: $(Src)"
@echo "Obj: $(Obj)"
2.3 头文件设计(process.h):接口函数声明
定义进度条回调函数类型和核心接口,方便后续扩展和复用:
cpp
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
// 定义进度条回调函数类型:适配不同场景的进度刷新逻辑
typedef void (*flush_t)(double total,double current,double speed,const char* userinfo);
// 彩色动态进度条核心接口
// total:总进度(如文件总大小)
// current:当前进度(如已下载大小)
// speed:当前速度(如MB/s)
// userinfo:附加信息(如单位)
void Process_version(double total,double current,double speed,const char*userinfo);
2.4 核心实现文件(process.c):关键逻辑代码剖析
集成颜色控制、进度计算、动态刷新,是进度条的核心:
cpp
#include"process.h"
#include<string.h>
// 进度条配置参数
#define SIZE 103 // 进度条缓冲区大小(适配100%+额外字符)
#define LABEL '=' // 进度填充字符
#define COLOR_GREEN "\033[32m" // 绿色
#define COLOR_GRAY "\033[90m" // 灰色
#define COLOR_CYAN "\033[36m" // 青色
#define COLOR_RESET "\033[0m" // 重置颜色
#define COLOR_RED "\033[31m" // 红色
#define COLOR_YELLOW "\033[33m" // 黄色
#define COLOR_BLUE "\033[34m" // 蓝色
#define COLOR_MAGENTA "\033[35m" // 品红
#define COLOR_WHITE "\033[97m" // 白色
#define COLOR_BLACK "\033[30m" // 黑色
// 彩色动态进度条实现
void Process_version(double total,double current,double speed,const char*userinfo)
{
// 边界处理:当前进度超过总进度时直接返回
if(current > total)
{
return;
}
// 动态光标:循环切换,提示程序运行中
static const char *lable="|/-\\";
static int index = 0; // 静态变量,记录光标位置,避免每次重置
int size = strlen(lable);
// 1. 计算比率
double rate = current * 100.0 / total;
char out_bar[SIZE];
memset(out_bar, '\0', sizeof(out_bar));
// 2. 填充进度字符
int i = 0;
for(; i < (int)rate; i++)
{
out_bar[i] = LABEL;
}
// 3. 彩色打印进度条(分颜色区分不同部分,视觉更清晰)
// printf("[%-100s][%5.1lf%%][%c] | %.1lf/%.1lf,speed: %.1lf%s\r", out_bar,rate,lable[index],current,total,speed,userinfo);
printf(COLOR_RED"[%-100s]",out_bar); // 进度主体(红色)
printf(COLOR_BLUE"[%5.1lf%%]",rate); // 百分比(蓝色)
printf(COLOR_CYAN"[%c]",lable[index]); // 动态光标(青色)
printf(COLOR_YELLOW"| %.1lf/%.1lf,speed: %.1lf%s\r",current,total,speed,userinfo); // 附加信息(黄色)
printf(COLOR_RESET); // 重置颜色,避免污染后续输出
// 手动刷新缓冲区,确保内容实时显示
fflush(stdout);
// 更新光标索引(循环切换)
index++;
index %= size;
// 4. 进度条完成,换行
if(current >= total)
{
printf("\r\n");
}
}
// 正常颜色进度条实现
//void Process_version(double total,double current,double speed,const char*userinfo)
//{
// if(current > total)
// {
// return;
// }
//
// static const char *lable="|/-\\";
// static int index = 0;
// int size = strlen(lable);
// // 1. 计算比率
// double rate = current * 100.0 / total;
// char out_bar[SIZE];
// memset(out_bar, '\0', sizeof(out_bar));
// // 2. 填充进度字符
// int i = 0;
// for(; i < (int)rate; i++)
// {
// out_bar[i] = LABEL;
// }
// // 3. 刷新进度条
// printf("[%-100s][%5.1lf%%][%c] | %.1lf/%.1lf,speed: %.1lf%s\r", out_bar,rate,lable[index],current,total,speed,userinfo);
// fflush(stdout);
// index++;
// index %= size;
//
// // 4. 进度条完成,换行
// if(current >= total)
// {
// printf("\r\n");
// }
//}
2.5 主函数模块(main.c):应用场景测试
模拟多场景下载任务,测试进度条的适配性和稳定性:
cpp
#include"process.h"
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
// 全局配置(可根据实际场景修改)
double gtotal = 1024.0; // 默认总进度(如1024MB)
double gspeed = 1.0; // 默认速度
// 模拟下载任务:调用进度条回调函数刷新进度
void Download(double total,flush_t cb)
{
double current = 0.0;
// 模拟不同网络速度(模拟实际场景中速度波动)
double level[] = {0.01,0.05,10.0,20.0,24.0,38.0,50.0,68.9};
int num = sizeof(level)/sizeof(level[0]);
while(1)
{
// 模拟下载耗时(1秒刷新一次)
usleep(1000000);
// 随机选择当前速度(模拟网络波动)
double speed = level[rand()%num];
current += speed;
// 边界处理:进度达到100%时终止
if(current >= total)
{
current = total; // 确保进度不超过100%
cb(total,current,speed,"MB/s");
break;
}
else{
// 刷新进度条
cb(total,current,speed,"MB/s");
}
}
// 这个跟proces.c中的第4步效果类似,写那个都行
//printf("\n");
}
int main()
{
// 初始化随机数种子,模拟速度波动
srand(time(NULL));
// 测试4个不同大小的下载任务,验证进度条适配性
printf("download: \n");
Download(gtotal,Process_version);
printf("download: \n");
Download(102.0,Process_version);
printf("download: \n");
Download(110.9,Process_version);
printf("download: \n");
Download(900.0,Process_version);
return 0;
}
三. 操作实践与效果展示
实际操作过程:
bash
[Lotso@VM-4-4-centos lesson12--Progress]$ ll
total 16
-rw-rw-r-- 1 Lotso Lotso 1620 Nov 28 10:27 main.c
-rw-rw-r-- 1 Lotso Lotso 344 Nov 28 08:55 Makefile
-rw-rw-r-- 1 Lotso Lotso 3078 Nov 28 10:22 process.c
-rw-rw-r-- 1 Lotso Lotso 533 Nov 28 10:22 process.h
[Lotso@VM-4-4-centos lesson12--Progress]$ make
compiling main.c to main.o
compiling process.c to process.o
main.o process.o link to process_bar
[Lotso@VM-4-4-centos lesson12--Progress]$ ./process_bar
download:
[====================================================================================================][100.0%][|]| 1024.0/1024.0,speed: 24.0MB/s
download:
[====================================================================================================][100.0%][\]| 102.0/102.0,speed: 50.0MB/s
download:
[====================================================================================================][100.0%][-]| 110.9/110.9,speed: 68.9MB/s
download:
[====================================================================================================][100.0%][/]| 900.0/900.0,speed: 24.0MB/s
[Lotso@VM-4-4-centos lesson12--Progress]$ make clean
File cleanup complete
执行./process_bar后,终端会输出 4 个下载任务的进度条,每个部分颜色区分清晰:
- 红色 :进度主体(
=填充部分); - 蓝色:百分比(如50.0%);
- 青色 :动态光标(
|/-\循环切换); - 黄色:附加信息(当前进度 / 总进度、传输速度);
进度完成后自动换行,后续任务不重叠,整体流畅无错乱。
注意:其中动态光标只与调用的这个函数有关,不管进度条动不动,他都是得转动的。还有就是大家如果想再优化一下的话可以试着把 = 替换成 色块 这样会更加清晰,其它的优化大家也可以自己想想尝试一下。
效果演示:
彩色动态进度条
结尾:
html
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!
结语:一个高质量的进度条,不仅是功能的补充,更是用户体验的提升。本文从基础原理到实战开发,再到优化扩展,带你吃透 Linux 终端进度条的核心逻辑,实现的彩色动态进度条可直接复用在下载、编译、数据处理等场景。掌握行缓冲区、回车换行、ANSI 颜色码等知识点后,你还可以举一反三,开发出更多终端交互工具(如倒计时、进度百分比动画)。Linux 终端开发的魅力就在于此 ------ 看似简单的功能,背后藏着扎实的底层逻辑,吃透这些细节,才能写出更稳定、更优雅的代码。
✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど
