往事
一秒钟
注:一秒钟是追求的目标。
静态字符串
rm -rf ~/donut && git clone -q https://gitcode.com/ZhangRelay1/donut.git && cd ~/donut && gcc helloworld.c -o helloworld && ./helloworld
动态甜甜圈
rm -rf ~/donut && echo "终端焦虑的甜甜圈" && git clone https://gitcode.com/ZhangRelay1/donut.git && cd ~/donut && g++ colordonut.cpp -o donut && ./donut && echo "突破封印的甜甜圈"
原版代码
字符串
#include <stdio.h>
int main() {
// ANSI 颜色格式:\033[属性;颜色m 内容 \033[0m
// 0m = 重置颜色,避免后续输出变色
// 红色 Hello
printf("\033[1;31mHello \033[0m");
// 绿色 World
printf("\033[1;32mWorld \033[0m");
// 黄色 !
printf("\033[1;33m!\033[0m\n");
return 0;
}
甜甜圈
#include <stdio.h>
#include <math.h>
#include <cstring>
#include <unistd.h>
int main() {
float A = 0, B = 0;
float i, j;
int k;
float z[1760];
char b[1760];
printf("\x1b[2J");
for (;;) {
memset(b, 32, 1760);
memset(z, 0, 7040);
for (j = 0; j < 6.28; j += 0.07) {
for (i = 0; i < 6.28; i += 0.02) {
float c = sin(i);
float d = cos(j);
float e = sin(A);
float f = sin(j);
float g = cos(A);
float h = d + 2;
float D = 1 / (c * h * e + f * g + 5);
float l = cos(i);
float m = cos(B);
float n = sin(B);
float t = c * h * g - f * e;
int x = 40 + 30 * D * (l * h * m - t * n);
int y = 12 + 15 * D * (l * h * n + t * m);
int o = x + 80 * y;
int N = 8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n);
if (22 > y && y > 0 && x > 0 && 80 > x && D > z[o]) {
z[o] = D;
b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];
}
}
}
printf("\x1b[H");
for (k = 0; k < 1761; k++) {
putchar(k % 80 ? b[k] : 10);
A += 0.00004;
B += 0.00002;
}
usleep(30000);
}
return 0;
}

终端彩色 Hello + ASCII 旋转甜甜圈 全套完整讲解 & 一键运行方案
一、整体结构梳理
两套程序:
- 静态彩色 HelloWorld(C 语言):ANSI 终端彩色输出,无依赖数学库,极简
- 动态 3D ASCII 甜甜圈(C++):三角函数渲染圆环动画,终端字符绘制 3D 效果 配套两条一键执行单行 shell 命令,分别对应静态字符串 / 动态甜甜圈
二、静态彩色 helloworld.c 完整解析
源码回顾
c
运行
#include <stdio.h>
int main() {
// ANSI 转义序列格式:\033[修饰符;前景色m 文本 \033[0m 重置样式
// \033 等价 \x1b ESC转义字符,终端识别控制码
// 1;31m:1=高亮加粗,31=红色前景
printf("\033[1;31mHello \033[0m");
// 1;32m 高亮绿色
printf("\033[1;32mWorld \033[0m");
// 1;33m 高亮黄色
printf("\033[1;33m!\033[0m\n");
return 0;
}
1. ANSI 颜色码对照表
表格
| 前景色 | 代码 | 样式标识 |
|---|---|---|
| 红 | 31 | 0 = 正常,1 = 加粗高亮 |
| 绿 | 32 | 4 = 下划线 |
| 黄 | 33 | 7 = 反色 |
| 蓝 | 34 | |
| 紫 | 35 | |
| 青 | 36 | |
| 白 | 37 |
\033[0m 必须加,重置所有颜色样式,否则终端后续输入文字都会带颜色。
2. 编译 & 运行命令
单行一键脚本(静态字符串)
bash
运行
git clone -q https://gitcode.com/ZhangRelay1/donut.git && cd ~/donut && gcc helloworld.c -o helloworld && ./helloworld
拆解分步说明:
git clone -q:静默克隆仓库,不输出多余日志cd ~/donut:进入家目录下克隆的仓库文件夹gcc helloworld.c -o helloworld:gcc 编译 C 程序,输出可执行文件 helloworld./helloworld:执行彩色打印程序
三、ASCII 3D 旋转甜甜圈 colordonut.cpp 深度讲解
核心原理
利用圆环参数方程 + 二维平面投影 + 光照明暗分级,把 3D 甜甜圈投射到 2D 终端字符画布,循环旋转两个角度 A、B 实现动画。
依赖头文件说明
cpp
运行
#include <stdio.h> // 终端输出putchar/printf
#include <math.h> // sin/cos三角函数,3D计算核心
#include <cstring> // memset 内存初始化,C++标准头
#include <unistd.h> // usleep 微秒延时,控制动画帧率
关键变量作用
cpp
运行
float A = 0, B = 0; // 两个旋转角度:A竖直旋转、B水平旋转,每次循环微量累加实现转动
float i, j; // 圆环两层循环参数(大圆+小圆)
int k;
float z[1760]; // Z深度缓存:记录每个屏幕坐标的景深,近处覆盖远处
char b[1760]; // 终端字符画布缓冲区,80列×22行=1760字符
关键控制转义码
c
运行
printf("\x1b[2J"); // \x1b = \033,2J清空整个终端屏幕
printf("\x1b[H"); // 光标移动到左上角(0,0),实现画面刷新不滚动
双层循环:生成甜甜圈曲面
cpp
运行
for (j = 0; j < 6.28; j += 0.07) // 外层圆环大圆 0~2π
for (i = 0; i < 6.28; i += 0.02) // 小圆截面 0~2π
通过 sin/cos 计算三维坐标,经过 A、B 角度旋转矩阵变换,完成 3D 旋转。
透视投影 & 深度缓存
c
运行
float D = 1 / (c * h * e + f * g + 5); // 透视缩放系数,模拟近大远小
int x = 40 + 30 * D * (...); // 投影到屏幕X坐标(横向偏移缩放)
int y = 12 + 15 * D * (...); // 投影到屏幕Y坐标(纵向偏移缩放)
int o = x + 80 * y; // 一维画布索引,二维坐标转一维数组下标
z[o] = D 深度缓冲逻辑:同一个屏幕像素只保留离镜头最近的点,避免画面重叠错乱。
明暗字符分级
c
运行
"N > 0 ? N : 0"
".,-~:;=!*#$@" // 字符从左到右亮度递增,模拟光照明暗
N 是光照强度,数值越大使用越密的字符,实现甜甜圈立体光影效果。
画面刷新与帧率控制
c
运行
usleep(30000); // 休眠30000微秒 = 30ms,控制动画流畅度
A += 0.00004;
B += 0.00002; // 每次画面轻微增加旋转角度,持续转动
动态甜甜圈一键执行命令
bash
运行
echo "终端焦虑的甜甜圈" && git clone https://gitcode.com/ZhangRelay1/donut.git && cd ~/donut && g++ colordonut.cpp -o donut -lm && ./donut && echo "突破封印的甜甜圈"
重点补充编译参数 -lm
代码大量使用 math.h 数学函数,Linux 下 g++ 编译必须加 -lm 链接数学库,否则会报sin/cos未定义报错。 原命令缺少会编译失败,修正完整版:
bash
运行
echo "终端焦虑的甜甜圈" && git clone https://gitcode.com/ZhangRelay1/donut.git && cd ~/donut && g++ colordonut.cpp -o donut -lm && ./donut && echo "突破封印的甜甜圈"
四、踩坑避坑全通关指南
坑 1:git 重复克隆报错
如果已经克隆过仓库,再次执行会提示文件夹已存在,解决方案二选一:
- 执行前删除旧目录:
bash
运行
rm -rf ~/donut && echo "终端焦虑的甜甜圈" && git clone https://gitcode.com/ZhangRelay1/donut.git && cd ~/donut && g++ colordonut.cpp -o donut -lm && ./donut && echo "突破封印的甜甜圈"
- 仅进入目录不重新克隆:
bash
运行
cd ~/donut && g++ colordonut.cpp -o donut -lm && ./donut
坑 2:甜甜圈编译报错 undefined reference to sin/cos
原因:未链接数学库,编译参数缺少 -lm,修正:g++ colordonut.cpp -o donut -lm
坑 3:Windows 终端无颜色 / 无清屏效果
ANSI 转义码仅 Linux/macOS Terminal、WSL、GitBash 支持;CMD 原生不兼容。
坑 4:动画卡顿 / 过快
修改 usleep(30000) 数值:数字越大延时越长,动画越慢,如 usleep(50000) 变慢。
坑 5:退出甜甜圈动画
终端按下快捷键 Ctrl + C 终止程序。
五、两种程序对比总结
表格
| 项目 | helloworld.c 静态彩色字符串 | colordonut.cpp 动态甜甜圈 |
|---|---|---|
| 语言 | C 语言 gcc 编译 | C++ g++ 编译 |
| 依赖 | stdio.h 无额外库 | math.h 必须加 -lm 链接库 |
| 效果 | 静态彩色文字,无动画 | 实时 3D 旋转 ASCII 动画 |
| 原理 | ANSI 终端颜色控制码 | 三维圆环参数方程 + 投影 + 深度缓存 |
| 资源占用 | 极低 | 持续循环计算,CPU 轻微占用 |
六、极简封装一键脚本(run_all.sh)
一次性整合静态 + 动态两种程序,按需执行
bash
运行
#!/bin/bash
rm -rf ~/donut
echo "=== 拉取代码仓库 ==="
git clone -q https://gitcode.com/ZhangRelay1/donut.git
cd ~/donut
echo -e "\n=== 运行静态彩色 HelloWorld ==="
gcc helloworld.c -o hello
./hello
rm -f hello
echo -e "\n=== 启动3D旋转甜甜圈动画 Ctrl+C退出 ==="
g++ colordonut.cpp -o donut -lm
./donut
授权运行:
bash
运行
chmod +x run_all.sh
./run_all.sh
Linux 终端 超多趣味一键运行案例(复制即跑,无需提前装环境,全单行命令)
前置说明
全部适配 Ubuntu / CentOS / WSL / MacOS 终端; Ctrl+C 退出动画;重复运行报错文件夹存在就加 rm -rf ~/funtmp 放最前面。
一、3D 旋转彩色甜甜圈(升级版带颜色,原版增强)
单行一键
bash
运行
rm -rf ~/donut && echo -e "\033[1;36m终端焦虑的彩色甜甜圈启动\033[0m" && git clone -q https://gitcode.com/ZhangRelay1/donut.git ~/donut && cd ~/donut && g++ colordonut.cpp -o donut -lm && ./donut && echo -e "\033[1;32m突破封印成功\033[0m"
二、终端下雪动画(纯 shell,不用编译,超浪漫)
一键单行
bash
运行
while true;do echo $LINES $COLUMNS $(for i in $(seq $RANDOM%$COLUMNS);do printf " ";done;echo '*')|awk '{system("clear");for(y=0;y<$1;y++){for(x=0;x<$2;x++){if(y==$3){printf "\033[1;37m*\033[0m"}else printf " "}print ""}}';sleep 0.1;done
三、终端爱心跳动(彩色动态爱心,表白神器)
C 语言一键编译运行单行
bash
运行
rm -rf ~/hearttmp && mkdir ~/hearttmp && cd ~/hearttmp && cat > heart.c <<EOF
#include <stdio.h>
#include <math.h>
#include <unistd.h>
int main(){
float x,y,a;
for(;;){
for(y=1.5f;y>-1.5f;y-=0.1f){
for(x=-1.5f;x<1.5f;x+=0.05f){
a=x*x+y*y-1;
if(a*a*a-x*x*y*y*y<0)printf("\033[1;31m*\033[0m");
else printf(" ");
}printf("\n");
}
usleep(100000);
printf("\033[H");
}
return 0;
}
EOF
gcc heart.c -o heart -lm && ./heart
四、黑客帝国数字雨(绿色字符流,装 X 神器)
纯 bash 无需编译,一键运行:
bash
运行
while :;do echo $LINES $COLUMNS $((RANDOM%2));sleep 0.05;done|awk '{system("clear");for(i=0;i<$1;i++){for(j=0;j<$2;j++){c=rand()*10;if($3==1&&rand()>0.97)printf("\033[1;32m%d\033[0m",c);else printf " "}print ""}}'
五、终端贪吃蛇游戏(C 语言完整版,方向键操控)
一键自动生成代码 + 编译 + 启动:
bash
运行
rm -rf ~/snaketmp && mkdir ~/snaketmp && cd ~/snaketmp && cat > snake.c <<EOF
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <termios.h>
#include <signal.h>
int width=20,height=20;
int x,y,fx,fy,score;
int tx[400],ty[400];
int len;
char dir='d';
struct termios old;
void setterm(){tcgetattr(0,&old);struct termios n=old;n.c_lflag&=~(ICANON|ECHO);tcsetattr(0,TCSANOW,&n);}
void resterm(){tcsetattr(0,TCSANOW,&old);}
void genfood(){fx=rand()%width;fy=rand()%height;}
void draw(){
system("clear");
for(int i=0;i<width+2;i++)printf("#");printf("\n");
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
if(j==0)printf("#");
if(i==y&&j==x)printf("\033[1;32mO\033[0m");
else if(i==fy&&j==fx)printf("\033[1;31mF\033[0m");
else {int hit=0;for(int k=1;k<len;k++)if(tx[k]==j&&ty[k]==i){printf("\033[1;34mo\033[0m");hit=1;break;}if(!hit)printf(" ");}
if(j==width-1)printf("#");
}printf("\n");
}
for(int i=0;i<width+2;i++)printf("#");printf("\n得分:%d\nWASD控制,Q退出",score);
}
void input(){
if(kbhit()){char c=getchar();if(c=='a'&&dir!='d')dir='a';if(c=='d'&&dir!='a')dir='d';if(c=='w'&&dir!='s')dir='w';if(c=='s'&&dir!='w')dir='s';if(c=='q'){resterm();exit(0);}}
}
int kbhit(){struct timeval t={0,0};fd_set f;FD_ZERO(&f);FD_SET(0,&f);return select(1,&f,NULL,NULL,&t);}
void logic(){
int px=tx[len-1],py=ty[len-1];
for(int i=len-1;i>0;i--){tx[i]=tx[i-1];ty[i]=ty[i-1];}
if(dir=='a')x--;if(dir=='d')x++;if(dir=='w')y--;if(dir=='s')y++;
if(x<0||x>=width||y<0||y>=height){resterm();printf("游戏结束!得分:%d\n",score);exit(0);}
for(int i=1;i<len;i++)if(tx[i]==x&&ty[i]==y){resterm();printf("撞到自己!得分:%d\n",score);exit(0);}
tx[0]=x;ty[0]=y;
if(x==fx&&y==fy){score+=10;len++;tx[len-1]=px;ty[len-1]=py;genfood();}
}
int main(){
setterm();srand(time(NULL));x=width/2;y=height/2;genfood();tx[0]=x;ty[0]=y;len=1;score=0;
while(1){draw();input();logic();usleep(100000);}
return 0;
}
EOF
gcc snake.c -o snake && ./snake
操作:W A S D 上下左右,Q 退出
六、终端 2048 小游戏(一键生成运行)
bash
运行
rm -rf ~/game2048 && mkdir ~/game2048 && cd ~/game2048 && cat > 2048.c <<EOF
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>
int mp[4][4];
void init(){
for(int i=0;i<4;i++)for(int j=0;j<4;j++)mp[i][j]=0;
srand((unsigned)time(NULL));
int a=rand()%4,b=rand()%4,c=rand()%4,d=rand()%4;
mp[a][b]=2;mp[c][d]=4;
}
void show(){
system("clear");
for(int i=0;i<4;i++){
for(int j=0;j<4;j++)printf("%4d",mp[i][j]);
printf("\n");
}
printf("W上 S下 A左 D右\n");
}
void randadd(){
int x[16],y[16],cnt=0;
for(int i=0;i<4;i++)for(int j=0;j<4;j++)if(mp[i][j]==0){x[cnt]=i;y[cnt]=j;cnt++;}
if(cnt==0)return;
int r=rand()%cnt;
mp[x[r]][y[r]]=rand()%2?2:4;
}
void left(){
for(int i=0;i<4;i++){
int t[4]={0},p=0;
for(int j=0;j<4;j++)if(mp[i][j]!=0)t[p++]=mp[i][j];
for(int j=0;j<3;j++)if(t[j]==t[j+1]&&t[j]!=0){t[j]*=2;t[j+1]=0;}
int tp=0;for(int j=0;j<4;j++)if(t[j]!=0)mp[i][tp++]=t[j];else mp[i][tp++]=0;
}
randadd();
}
void right(){for(int i=0;i<4;i++){int t[4]={0},p=3;for(int j=3;j>=0;j--)if(mp[i][j])t[p--]=mp[i][j];for(int j=0;j<3;j++)if(t[j]==t[j+1]&&t[j]){t[j]*=2;t[j+1]=0;}int tp=3;for(int j=3;j>=0;j--)if(t[j])mp[i][tp--]=t[j];else mp[i][tp--]=0;}randadd();}
void up(){for(int j=0;j<4;j++){int t[4]={0},p=0;for(int i=0;i<4;i++)if(mp[i][j])t[p++]=mp[i][j];for(int i=0;i<3;i++)if(t[i]==t[i+1]&&t[i]){t[i]*=2;t[i+1]=0;}int tp=0;for(int i=0;i<4;i++)if(t[i])mp[tp++][j]=t[i];else mp[tp++][j]=0;}randadd();}
void down(){for(int j=0;j<4;j++){int t[4]={0},p=3;for(int i=3;i>=0;i--)if(mp[i][j])t[p--]=mp[i][j];for(int i=0;i<3;i++)if(t[i]==t[i+1]&&t[i]){t[i]*=2;t[i+1]=0;}int tp=3;for(int i=3;i>=0;i--)if(t[i])mp[tp--][j]=t[i];else mp[tp--][j]=0;}randadd();}
int main(){
char op;init();
while(1){show();op=getchar();
if(op=='a')left();if(op=='d')right();if(op=='w')up();if(op=='s')down();
}
return 0;
}
EOF
gcc 2048.c -o game2048 && ./game2048
七、终端时钟(彩色动态电子表)
bash
运行
while true;do clear;t=$(date "+%H:%M:%S");echo -e "\033[1;35m==== $t ====\033[0m";sleep 1;done
八、彩虹文字刷屏(彩色滚动横幅)
bash
运行
text="Linux终端趣味一键案例";while true;do for c in 31 32 33 34 35 36 37;do echo -e "\033[1;${c}m$text\033[0m";sleep 0.1;clear;done;done
使用小贴士
- 想清空残留文件夹:命令开头加
rm -rf ~/xxx(xxx 对应目录名 donut/hearttmp/snaketmp) - 退出所有动画 / 游戏:
Ctrl + C - 编译报错找不到 math 函数:编译命令必须带
-lm - WSL / Mac 全部兼容;纯 Windows CMD 无彩色效果,建议用 Windows Terminal
本文介绍了两种有趣的终端程序实现:静态彩色Hello World和动态3D ASCII甜甜圈动画。文章详细解析了二者的代码原理,包括ANSI转义序列实现终端彩色输出、3D渲染算法以及终端动画控制技术。同时提供了完整的一键运行脚本,并对常见问题进行了解答。此外,还延伸介绍了多种终端趣味程序,如下雪动画、爱心跳动、贪吃蛇游戏等,均附有可直接执行的单行命令。文中强调了对数学库的链接(-lm)和终端兼容性等关键注意事项,为读者提供了完整的终端趣味编程实践方案。
1172

被折叠的 条评论
为什么被折叠?



