如何用C语言实现类似apt-get的底部固定终端进度条?
C语言实现终端底部固定进度条(类似apt-get效果)
核心原理
要实现固定在终端底部的进度条,核心是利用ANSI终端控制序列操控光标位置、清除行内容,同时区分顶部日志输出区和底部进度条区:
- 提前获取终端窗口高度,计算进度条固定的底部行位置
- 输出顶部日志时,先将光标移至日志区域,输出完成后切回底部更新进度条
- 用光标保存/恢复指令简化位置计算,避免日志覆盖进度条
完整代码示例
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/ioctl.h> #include <signal.h> // 存储终端行数,用于定位进度条位置 int terminal_rows = 0; // 获取终端窗口大小 void get_terminal_size() { struct winsize ws; if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { terminal_rows = ws.ws_row; } else { // 终端信息获取失败时,使用默认行数 terminal_rows = 24; } } // 处理窗口大小变化信号 void handle_resize(int sig) { get_terminal_size(); } // 输出顶部日志内容,不覆盖底部进度条 void print_log(const char *msg) { // 保存当前光标位置(进度条位置) printf("\033[s"); // 光标上移1行,回到日志输出区 printf("\033[%dA", 1); // 输出日志 printf("%s\n", msg); // 恢复光标到进度条位置 printf("\033[u"); // 强制刷新输出缓冲区,确保内容立即显示 fflush(stdout); } // 更新底部进度条 void update_progress(int percentage) { struct winsize ws; ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); int width = ws.ws_col; int bar_width = width - 10; // 预留百分比显示的空间 // 移动光标到终端最后一行开头 printf("\033[%d;1H", terminal_rows); // 清除当前行内容 printf("\033[K"); // 绘制进度条主体 printf("["); for (int i = 0; i < bar_width; i++) { printf(i < (percentage * bar_width) / 100 ? "=" : " "); } printf("] %d%%", percentage); // 强制刷新缓冲区 fflush(stdout); } int main() { // 初始化终端大小 get_terminal_size(); // 注册窗口大小变化信号,实时更新终端行数 signal(SIGWINCH, handle_resize); // 模拟任务执行过程 for (int i = 0; i <= 100; i++) { // 每10%进度输出一条日志 if (i % 10 == 0) { char log_msg[100]; snprintf(log_msg, sizeof(log_msg), "正在处理任务:%d/100", i); print_log(log_msg); } // 更新进度条 update_progress(i); // 模拟任务耗时 usleep(50000); } // 进度完成后换行,让后续输出恢复正常 printf("\n"); return 0; }
代码关键点解释
终端大小获取与更新
- 通过
ioctl(TIOCGWINSZ)获取终端的行数和列数,这是定位进度条到底部的核心依据 - 注册
SIGWINCH信号处理函数,窗口大小变化时实时更新终端行数,避免进度条位置偏移
- 通过
日志输出逻辑
- 用
\033[s保存进度条位置的光标,输出日志前上移1行避开进度条,输出完成后用\033[u恢复光标,确保日志始终在进度条上方累加
- 用
进度条绘制逻辑
\033[%d;1H将光标直接定位到终端最后一行开头\033[K清除当前行内容,避免旧的进度条残留- 动态计算进度条填充长度,适配终端宽度
ANSI转义序列说明
\033[s:保存当前光标位置\033[u:恢复之前保存的光标位置\033[nA:光标向上移动n行\033[n;1H:将光标移动到第n行第1列\033[K:清除从光标位置到行尾的内容
注意事项
- 确保终端支持ANSI转义序列:现代终端(xterm、gnome-terminal等)均支持,Windows 10及以上需开启虚拟终端支持
- 必须手动刷新缓冲区:用
fflush(stdout)强制输出,避免内容因行缓冲延迟显示 - 动态适配终端宽度:示例中已实现用
ws.ws_col获取终端宽度,进度条会自动适配窗口大小
内容的提问来源于stack exchange,提问作者cyicz123
相关产品推荐
相关产品推荐

