You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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;
}

代码关键点解释

  1. 终端大小获取与更新

    • 通过ioctl(TIOCGWINSZ)获取终端的行数和列数,这是定位进度条到底部的核心依据
    • 注册SIGWINCH信号处理函数,窗口大小变化时实时更新终端行数,避免进度条位置偏移
  2. 日志输出逻辑

    • 用\033[s保存进度条位置的光标,输出日志前上移1行避开进度条,输出完成后用\033[u恢复光标,确保日志始终在进度条上方累加
  3. 进度条绘制逻辑

    • \033[%d;1H将光标直接定位到终端最后一行开头
    • \033[K清除当前行内容,避免旧的进度条残留
    • 动态计算进度条填充长度,适配终端宽度
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 18:09:27