TL;DR (太长不看)
本文梳理了开发、测试与底层联调中最常用的 76 个 Linux 核心命令。为了拒绝枯燥的死记硬背,文章按“实际工作场景”进行了分类,并附带了最直观的执行效果说明。文末附赠我自己写的一个 Linux 交互式沙盒特训脚本,方便在终端里沉浸式复习。

在真实的硬件联调、边缘计算节点部署以及后端服务的开发中,熟练掌握 Linux 命令是排查奇葩问题(如端口冲突、网络丢包、内存溢出)的底层底气。以下是我整理的高频速查字典。

📁 一、 目录与文件流转 (基石操作)

实际工作场景 核心命令语句 实际效果 / 备注
迷失在终端,想确认当前绝对路径 pwd 输出当前所在文件夹的完整路径坐标。
新建目录存放模型或代码 mkdir models 生成名为 models 的空文件夹。
查看隐藏文件及读写权限 ls -la 列出详细文件列表,包含权限和时间戳。
以树状图可视化代码工程结构 tree . 像 IDE 一样直观俯瞰目录层级。
切换到指定的日志文件夹 cd project/logs 成功进入目标目录。
创建空文件 / 更新文件时间戳 touch config.yaml 分配 0KB 新文件或更新现有文件的修改时间。
备份一份配置文件以防改坏 cp a.txt b.txt 复制并生成一份完全一致的克隆文件。
给文件改名或移动文件路径 mv a.txt b.txt 原文件被移动或重命名。
删除不需要的空目录 rmdir empty_dir 安全移除空文件夹(非空会报错拦截)。
强制删除文件或目录,毫不留情 rm -rf node_ips.txt 高危:彻底从磁盘抹除,无法恢复。
为深层目录文件创建快捷方式 ln -s a.txt b.txt 创建软链接,修改 b 等于修改 a。
探查文件的底层 Inode 和极精确时间 stat a.txt 深入系统底层,查看文件详细存储状态。
全局搜索找不到的日志文件 find . -name "*.log" 遍历子目录,揪出匹配的文件路径。

📝 二、 文本查看与日志排错 (调试神器)

实际工作场景 核心命令语句 实际效果 / 备注
快速向文件中写入一行文本 echo "Init" > a.log 文本被强行写入并保存到文件中。
一次性把小文件内容全打在屏幕上 cat a.log 内容被全部加载并倾倒在终端。
代码文件太长,想用方向键慢慢翻页 less main.py 交互式阅读,按 q 退出,不刷屏。
只想看文件的前 5 行 head -n 5 main.py 仅读取文件头部。
只想看日志的最后 1 行 tail -n 1 a.log 精确定位到文件末尾。
程序运行中,实时盯盘最新日志 tailf a.log 等同 tail -f,屏幕随新日志实时滚动更新。
日志成千上万,只过滤出报错行 grep "Error" a.log X光级过滤,仅显示包含关键字的行。
把文本流中的 timeout 全局替换为大写 sed 's/timeout/TIMEOUT/g' a.log 瞬间完成正则查找与字符替换。
提取每行用空格隔开的第二列数据 awk '{print $2}' a.log 精准切片提取所需数据列。
把找出的文件传给 grep 搜错误 find . -name “*.log” | xargs grep “Error” 面试高频:管道与 xargs 完美结合。

📊 三、 系统资源与进程监控 (性能压测)

实际工作场景 核心命令语句 实际效果 / 备注
评估内存够不够跑模型 (OOM排查) free -m 以 MB 展示物理内存和 Swap 交换区剩余量。
排查硬盘是不是被数据塞满了 df -h 列出所有挂载硬盘设备的剩余可用比例。
计算当前工程目录占了多大空间 du -sh . 递归计算当前目录的总体积。
查服务器开机多久,及 CPU 拥挤程度 uptime 展示开机时间及 1/5/15 分钟的系统负载。
确认当前系统内核版本和 CPU 架构 uname -a 暴露底层系统指纹信息 (x86/ARM 等)。
打印当前系统时间 (校准定时任务) date 输出标准系统时间。
拍一张后台运行进程的静态快照 ps aux 展示所有进程 PID 和后台执行命令。
赛博朋克风动态监控 CPU 与内存 htop 相比 top 更直观,可视化核心负载跳动。
程序卡死,根据 PID 强行结束它 kill -9 9999 发送 SIGKILL,内核直接回收进程资源。
下班关 SSH 后,让程序继续在后台跑 nohup python main.py & 脱离终端束缚,后台静默运行并收集日志。

🌐 四、 网络通信与端口排查 (联调必备)

实际工作场景 核心命令语句 实际效果 / 备注
查看网卡局域网/公网 IP 地址 ip addr / ifconfig 展示 eth0/wlan0 等网卡的 IP 信息。
测本机到目标节点的网络延迟 ping -c 3 8.8.8.8 发送 ICMP 请求,计算网络延迟(ms)。
排查本机开放了哪些 TCP/UDP 端口 ss -tulnp / netstat -tulnp 硬件联调排雷神器,列出监听端口及对应 PID。
报错“端口被占用”,揪出流氓程序 lsof -i :80 精准查出占用该端口的进程,方便后续 kill。
服务器无浏览器,直接下载网络文件 wget https://xx.com/a.zip 建立下载流,将远程文件拉取到硬盘。
模拟 HTTP 客户端请求,测后端接口 curl -I https://api.xxx 获取目标服务器返回的 HTTP 响应头。
不发数据,仅探测对方 TCP 端口是否开放 nc -zv 192.168.1.1 22 瞬间测出对方防火墙是否放行了某端口。
命令行查看 WiFi/有线网络连接状态 nmcli d 现代网络管理器,直观列出网卡连接状态。

🔐 五、 权限管控与跨端传输

实际工作场景 核心命令语句 实际效果 / 备注
赋予脚本可执行权限 chmod 755 run.sh 脚本获得执行权 (x),可直接 ./ 运行。
把文件所有权移交给 root chown root:root a.txt 文件易主,普通用户无法再随意修改。
借用最高管理员权限执行命令 sudo cat /etc/shadow 输入密码临时提权,越权访问核心文件。
新增系统用户并分配 Home 目录 useradd -m dev 创建名为 dev 的新系统账号。
修改用户登录密码 passwd dev 交互式修改加密密码表。
彻底删除离职用户及个人文件夹 userdel -r dev 从服务器中清理干净。
把工程打成压缩包,方便高速传输 tar -czvf app.tar.gz ./app/ 聚合成包裹并用 gzip 压缩体积。
打包成 / 解压兼容 Windows 的 ZIP zip -r a.zip ./ / unzip a.zip 处理跨平台常用的 ZIP 格式压缩包。
生成免密登录 / Git 推送的公私钥对 ssh-keygen -t rsa ~/.ssh 生成身份凭证。
安全加密远程登录到另一台服务器 ssh root@192.168.1.1 本地接管远端服务器的 Shell。
把本地文件通过网络飞传到另一台机器 scp a.tar.gz root@IP:/tmp/ 基于 SSH 的加密跨节点文件传输。
极速增量同步代码 (只传差异文件) rsync -avz ./app/ root@IP:/ 高效保持本地与远端代码目录一致性。

🛠️ 六、 硬件磁盘与高级运维

实际工作场景 核心命令语句 实际效果 / 备注
看系统底层识别到的物理硬盘拓扑 lsblk 插 U 盘找挂载路径必备。
查看底层磁盘物理分区信息 fdisk -l 切分新硬盘的入口命令。
将物理硬盘强行挂载绑定到某个文件夹 mount /dev/sdb1 /mnt/data 绑定后,往文件夹写数据等于写入该硬盘。
拔出 U 盘前,解除软件挂载绑定 umount /mnt/data 确保底层读写缓存刷入磁盘,安全拔出。
查看当前连接这台服务器的所有人员 who / w 后者更强,能看到每个人正在执行什么命令。
忘了昨天敲过什么长命令,调出历史 history | tail -n 10 ~/.bash_history 调出最近敲过的指令。
查看底层防火墙链 / UFW 端口放行状态 iptables -L / ufw status 排查外部访问不通是不是被防火墙 DROP 了。
命令系统底层重新启动某后台服务 systemctl restart nginx 触发 init 系统重新拉起守护进程。
服务启动失败,调出底层崩溃日志 journalctl -u nginx.service 查看服务的堆栈报错细节,定位原因。
编辑 / 查看定时计划任务 (如每日备份) crontab -e / crontab -l 配置 Cron 守护进程 (分 时 日 月 周)。
测试硬盘极限纯写入速度 / 烧录镜像 dd if=/dev/zero of=test bs=1M count=100 绕开文件系统直接写盘,输出真实 I/O 速度。
进入 MySQL / PG 数据库交互终端 mysql -u root -p / psql -U postgres 进入专用的 REPL 环境执行 SQL。
安全断电关机 / 重启系统 shutdown -h now / reboot 执行完整的卸载磁盘及停机/热重启流程。

🎁 彩蛋:自研交互式 Linux 实战沙盒

光看表格容易“眼会手不会”。为了复习,我自己用 Shell 写了一个带全局检索、序号跳转、无痕沙盒的交互式教学脚本软件。

如果你想在真实的终端里过一遍这 76 个命令,感受“敲下回车 -> 看到系统状态变化”的闭环反馈,可以将这个脚本保存在你的机器上运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/bin/bash

# ======================================================
# Linux 核心全栈命令:专业级交互式特训系统 (带主菜单与检索)
# ======================================================

GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
PURPLE='\033[0;35m'
RED='\033[0;31m'
NC='\033[0m'

# 1. 初始化沙盒与题库数据库
LAB_DIR="/tmp/linux_pro_lab_$$"
DB_FILE="/tmp/linux_db_$$.txt"

mkdir -p "$LAB_DIR/project/logs" "$LAB_DIR/project/src"
echo "print('HIL edge node ready')" > "$LAB_DIR/project/src/main.py"
echo "192.168.1.100 dsp-node" > "$LAB_DIR/project/hosts.txt"
echo "Error: connection timeout" > "$LAB_DIR/project/logs/error.log"

# 构建带序号的本地数据库 (过滤空行和注释,自动生成递增序号)
grep -vE '^(#|$)' << 'EOF' | awk -F'|' '{print NR"|"$0}' > "$DB_FILE"
# --- 目录与文件 ---
迷失在终端时,想知道当前的绝对路径|pwd|输出你当前所在文件夹的完整路径坐标。|0|
需要新建一个目录存放模型或代码|mkdir models|在当前位置生成了一个名为 models 的空文件夹。|0|
想查看当前目录下所有文件及隐藏文件的读写权限|ls -la|列出详细文件列表,明确谁有权限读写它们。|0|
想以树状图的形式直观了解代码工程结构|tree .|像 IDE 一样可视化俯瞰整个目录层级。|1|├── project\n│ ├── hosts.txt\n│ └── logs
切换到指定的日志文件夹中|cd project/logs && pwd|成功进入目标目录,后续操作都在此目录下进行。|0|
新建一个空文件,或更新现有文件的时间戳|touch new_config.yaml|硬盘上被分配了一个 0KB 的新文件,等待写入。|0|
修改配置文件前,先复制备份一份以防万一|cp ../hosts.txt ./hosts_backup.txt|生成了一份完全一致的克隆文件。|0|
给文件改个名字|mv hosts_backup.txt node_ips.txt|原文件被重命名为 node_ips.txt。|0|
删除不需要的空目录|mkdir empty_dir && rmdir empty_dir|安全移除了这个空文件夹。|0|
强制删除文件,毫不留情|rm -rf node_ips.txt|该文件从磁盘中彻底抹除,无法恢复。|0|
想给深层目录下的文件建一个当前目录的快捷方式|ln -s ../hosts.txt shortcut.txt|生成了一个指针,修改快捷方式等于修改原文件。|0|
想知道文件极其底层的创建时间、大小和 inode 编号|stat ../hosts.txt|深入底层,看到了文件在系统硬盘上的精确微秒记录。|0|
不知道日志文件藏在哪,想在全局找出来|find . -name "*.log"|遍历所有子目录,揪出匹配的文件路径。|0|
# --- 文本与日志 ---
快速向文件中写入一行文本|echo "System init" > run.log|这行文本被直接写入并保存到了 run.log 中。|0|
一次性把文件里的内容全部打印到屏幕上|cat run.log|文件内容被加载并倾倒在终端里。|0|
代码文件太长,想用方向键慢慢滚动翻页阅读|less ../src/main.py|进入交互阅读模式,按 q 退出,不会导致刷屏。|1|print('HIL edge node ready')\n(END)
只想看代码文件的前 2 行|head -n 2 ../src/main.py|非常克制地只读取文件头部。|0|
只想看日志文件的最后 1 行|tail -n 1 run.log|直接略过前面,精确定位到最后一条日志。|0|
程序正在运行,你想实时盯盘看最新打出的日志|tailf run.log|终端进入监听状态,屏幕随新日志实时滚动。|1|System init\n[等待新日志...]
日志成千上万行,只想把包含报错的行抽离出来|grep "timeout" error.log|如同 X 光过滤,只显示包含该关键词的行。|0|
在文本输出流中,把小写的 timeout 批量替换为大写|sed 's/timeout/TIMEOUT/g' error.log|瞬间完成字符的查找与替换。|0|
像切西瓜一样,只提取文本中用空格隔开的第二列单词|awk '{print $2}' error.log|精准切片提取了所需的数据列。|0|
把 find 找到的文件列表,自动传给 grep 搜索错误内容|find . -name "*.log" | xargs grep "Error"|将前一个命令的输出作为后一个命令的参数,实现全局搜错。|1|./error.log: Error: connection timeout
# --- 系统监控 ---
评估当前物理内存还够不够跑大模型|free -m|以 MB 为单位展示内存和 Swap 交换区的剩余量。|0|
排查硬盘空间是不是快被数据填满了|df -h|列出所有挂载的硬盘设备及剩余可用比例。|0|
想知道当前这个工程目录一共占了多少 MB|du -sh .|递归计算了当前目录的总体积。|0|
查看服务器连续开了多久,以及 CPU 拥挤程度|uptime|展示开机时间与 1/5/15 分钟的负载指数。|0|
确认当前所处的系统内核版本和架构|uname -a|暴露出底层的系统指纹信息。|0|
排查系统的当前时间是否准确|date|打印出当前系统时间,用于校准定时任务。|0|
拍一张系统当前正在运行的进程快照|ps aux | head -n 3|展示了所有进程的 PID (编号) 和后台执行命令。|0|
进入类似 Windows 任务管理器的动态监控面板|htop|直观看到 CPU 每颗核心的负载跳动和内存消耗。|1|[|||| 15.0%] Tasks: 45, 1 running\nMem[|||||| 2.1G/16G]
某个程序卡死了,根据 PID 强制结束它|kill -9 9999|系统内核直接强硬回收了该进程占用的所有资源。|1|[1]+ Killed python main.py
下班关电脑后,依然让程序在服务器后台保持运行|nohup python main.py &|程序脱离终端束缚,在后台静默运行并将日志存入 nohup.out。|1|nohup: ignoring input and appending output to 'nohup.out'
# --- 网络与通信 ---
查看边缘节点的局域网或公网 IP 地址|ip addr|展示 eth0/wlan0 等网卡的 IP 信息。|0|
(旧版) 查看网卡及 IP 配置|ifconfig|这是旧版 Linux 最常用的网络配置展示工具。|1|eth0: flags=4163<UP,BROADCAST,RUNNING> mtu 1500...
测试本机到目标 IP 的网络延迟是否通畅|ping -c 2 8.8.8.8|发出 ICMP 请求,计算出网络延迟(ms)。|1|64 bytes from 8.8.8.8: icmp_seq=1 time=12.4 ms
排查本机开放了哪些 TCP/UDP 端口|ss -tulnp|列出系统所有监听的端口及对应的程序 PID。|1|udp UNCONN 0 0 0.0.0.0:5000 0.0.0.0:*
(旧版) 打印当前网络监听状态与端口|netstat -tulnp|列出监听状态的端口列表。|1|tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
服务报错“端口已被占用”,揪出是哪个程序占的|lsof -i :80|精准查出占用该端口的流氓程序,方便后续 kill。|1|COMMAND PID USER FD TYPE DEVICE NODE NAME\nnginx 12345 root 6u IPv4 88888 0t0 TCP *:80 (LISTEN)
直接用命令行下载网络文件|wget https://example.com/data.zip|建立下载流,将远程文件拉取到本地硬盘。|1|Saving to: 'data.zip'
模拟 HTTP 客户端请求,测试后端接口是否连通|curl -I https://api.github.com|收到了目标服务器返回的 HTTP 状态码响应头。|1|HTTP/2 200 \nserver: GitHub.com
不发送实际数据,仅探测对方服务器某端口是否开放|nc -zv 192.168.1.100 22|瞬间测出对方防火墙是否放行了该 TCP 端口。|1|Connection to 192.168.1.100 22 port [tcp/ssh] succeeded!
命令行查看网络连接状态(WiFi/有线)|nmcli d|直观列出所有网卡当前连接的网络名称。|1|DEVICE TYPE STATE CONNECTION\neth0 ethernet connected Wired 1
# --- 权限与传输 ---
给脚本赋予可执行权限|chmod 755 run.log|脚本拥有了执行权(x),可以直接 ./ 运行。|0|
把文件的所有权移交给系统的最高管理员|chown root:root run.log|文件的所有权易主,普通用户无法再随意修改。|1|
当前权限不够,临时借用最高权限执行命令|sudo cat /etc/shadow|输入密码提权后,成功越权访问了核心文件。|1|[sudo] password for user: \nroot:$6$xyz...
分配一个专属账号给新用户|useradd -m developer|为其分配了专属的 /home/developer 目录和账号。|1|
设置或修改登录密码|passwd developer|交互式修改了系统后端的加密密码表。|1|Changing password for user developer.
彻底删除系统用户及其文件夹|userdel -r developer|连同账号和文件彻底从服务器中清理干净。|1|
把代码打成一个压缩包,方便高速传输|tar -czvf project.tar.gz ../project/|将目录打成包裹并用 gzip 压缩了体积。|1|project/\nproject/src/main.py
打包成兼容 Windows 系统的 ZIP 格式|zip -r output.zip ../project/|生成了常规的 ZIP 压缩文件。|1|adding: project/ (stored 0%)
解压拿到的 ZIP 压缩包|unzip output.zip|还原了 ZIP 内部的完整目录层级。|1|Archive: output.zip\n inflating: project/src/main.py
生成公钥和私钥对(免密登录必备)|ssh-keygen -t rsa -b 4096|在 ~/.ssh 目录下生成了密钥身份凭证。|1|Generating public/private rsa key pair...\nYour identification has been saved.
安全地加密远程登录到另一台服务器|ssh root@192.168.1.100|本地终端接管了远端服务器的 Shell。|1|root@192.168.1.100's password:
将本地的文件通过网络飞传到另一台服务器|scp project.tar.gz root@192.168.1.100:/tmp/|通过 SSH 加密通道完成了点对点的文件传输。|1|project.tar.gz 100% 45KB 1.2MB/s
每次只传输被修改过的差异文件,增量极速同步代码|rsync -avz ../project/ user@192.168.1.100:/backup|极高效率地保持了本地与远端代码目录的一致性。|1|sending incremental file list...\nsrc/main.py
# --- 高阶硬件与运维 ---
查看系统底层识别到的物理硬盘拓扑图|lsblk|清晰列出 sda 等物理块设备的挂载分支。|1|NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT\nsda 8:0 0 50G 0 disk
准备给新买的空硬盘进行物理切分|fdisk -l|展示柱面、扇区等最底层信息,是切分硬盘的入口。|1|Disk /dev/sda: 50 GiB, 53687091200 bytes...
将物理硬盘强行挂载绑定到一个文件夹|mount /dev/sdb1 /mnt/data|绑定后,往文件夹里存数据就等于存入该硬盘。|1|
准备物理拔出 U 盘前,先解除软件挂载绑定|umount /mnt/data|解除了逻辑绑定,现在可以安全物理拔出设备。|1|
查看当前连接这台服务器的所有登录人员|who|列出了所有在线用户的终端和来源 IP。|1|root pts/0 2024-03-24 10:15 (192.168.1.5)
想知道在线的人此时此刻正在敲什么命令|w|暴露了当前所有人正在运行的活动进程。|1|USER TTY FROM WHAT\nroot pts/0 192.168.1.5 w
忘了昨天敲过什么超长命令,调出历史记录|history | tail -n 5|从 ~/.bash_history 调出了你最近敲过的指令。|1| 1001 docker restart nginx
服务器外部访问不通,查看底层防火墙规则|iptables -L|排查网络包是否被底层的 DROP 规则拦截了。|1|Chain INPUT (policy ACCEPT)\ntarget prot opt source destination
查看更容易理解的 UFW 防火墙端口放行状态|ufw status|直观展示哪些端口(如 22) 是 ALLOW 开放状态。|1|Status: active\n22/tcp ALLOW Anywhere
命令系统底层重新启动该后台服务|systemctl restart nginx|触发 init 系统重新拉起了守护进程。|1|
服务启动失败了,调出内核底层的崩溃日志定位原因|journalctl -u nginx.service -n 5|调出了服务的堆栈报错细节。|1|Mar 24 10:20:00 node nginx[123]: [emerg] bind() to 0.0.0.0:80 failed
[设置]配置每日/每小时自动运行的脚本任务|crontab -e|打开文本编辑器,写入 0 2 * * * /backup.sh 实现每日凌晨2点运行备份。|1|crontab: installing new crontab
[查看]确认当前系统里配置了哪些自动执行的计划任务|crontab -l|展示系统已配置的 Cron 守护进程定时任务表。|1|0 2 * * * /backup.sh
测试当前硬盘的极致纯写入速度,或烧录镜像|dd if=/dev/zero of=test.img bs=1M count=100|绕开文件系统直接写盘,输出了真实的 I/O 速度。|1|104857600 bytes (100 MB) copied, 0.12 s, 874 MB/s
在终端中进入 MySQL 数据库的交互控制台|mysql -u root -p|进入了专用的 REPL 环境,可以开始敲 SQL 语句了。|1|Welcome to the MySQL monitor.\nmysql>
在终端中进入 PostgreSQL 数据库的交互控制台|psql -U postgres|进入了 PG 数据库的交互环境。|1|psql (14.5)\npostgres=#
向所有进程发送终止信号,安全卸载磁盘并关机|shutdown -h now|系统开始执行断电保护停机流程。|1|System is going down for halt NOW!
系统内核更新后,执行完整的热重启流程|reboot|服务器断开连接,正在重新启动。|1|Restarting system.
EOF

TOTAL_CMDS=$(wc -l < "$DB_FILE")

# 打字机特效
type_out() {
local text="$1"
for (( i=0; i<${#text}; i++ )); do
echo -en "${text:$i:1}"
sleep 0.03
done
echo ""
}

# 退出清理机制
cleanup() {
cd /tmp || exit
rm -rf "$LAB_DIR" "$DB_FILE"
clear
echo -e "${GREEN}👋 已退出特训系统,沙盒环境已清理。${NC}"
exit 0
}
trap cleanup SIGINT SIGTERM

# ======================================================
# 功能界面模块
# ======================================================
search_cmd() {
read -p ">> 输入要搜索的命令关键字: " kw < /dev/tty
echo -e "\n${CYAN}---------- 搜索结果 ----------${NC}"
grep -i "$kw" "$DB_FILE" | awk -F'|' -v C="$CYAN" -v Y="$YELLOW" -v NC="$NC" '{printf C"[%02d] "Y"%-20s"NC" | 场景: %s\n", $1, $3, $2}'
echo -e "${CYAN}------------------------------${NC}"
read -p "按 [Enter] 返回主菜单..." < /dev/tty
}

search_scene() {
read -p ">> 输入要搜索的场景关键字: " kw < /dev/tty
echo -e "\n${CYAN}---------- 搜索结果 ----------${NC}"
grep -i "$kw" "$DB_FILE" | awk -F'|' -v C="$CYAN" -v Y="$YELLOW" -v NC="$NC" '{printf C"[%02d] "NC"场景: %-30s | "Y"命令: %s\n", $1, $2, $3}'
echo -e "${CYAN}------------------------------${NC}"
read -p "按 [Enter] 返回主菜单..." < /dev/tty
}

list_all() {
echo -e "\n${CYAN}---------- 全景命令库 (${TOTAL_CMDS}条) ----------${NC}"
awk -F'|' -v C="$CYAN" -v Y="$YELLOW" -v NC="$NC" '{printf C"[%02d] "Y"%-20s"NC" | 场景: %s\n", $1, $3, $2}' "$DB_FILE" | more
echo -e "${CYAN}------------------------------------------${NC}"
read -p "按 [Enter] 返回主菜单..." < /dev/tty
}

# ======================================================
# 实战沙盒模拟器 (带跳转引擎)
# ======================================================
run_simulation() {
cd "$LAB_DIR" || exit
local current_id=1

while [ "$current_id" -le "$TOTAL_CMDS" ]; do
# 根据序号提取数据
local line=$(awk -F'|' -v id="$current_id" '$1==id {print $0}' "$DB_FILE")
IFS='|' read -r id scene cmd effect is_sim sim_out <<< "$line"

clear
echo -e "${CYAN}======================================================${NC}"
echo -e "${PURPLE}▶ 进度标尺: [ $id / $TOTAL_CMDS ]${NC}"
echo -e "${YELLOW}💡 场景: ${scene}${NC}\n"

# 自动输入命令
echo -en "${GREEN}➜ edge-node:~/project\$ ${NC}"
type_out "$cmd"

# 第一层询问:执行还是退出
read -p "$(echo -e "\n${PURPLE}[按 Enter 键执行,或输入 'q' 退出模拟]${NC} >> ")" exec_input < /dev/tty
if [[ "$exec_input" == "q" || "$exec_input" == "Q" ]]; then
break
fi

echo -e "${CYAN}------------------------------------------------------${NC}"
# 执行结果
if [ "$is_sim" == "1" ]; then
if [ -n "$sim_out" ]; then echo -e "$sim_out\n"; fi
else
eval "$cmd" > /tmp/cmd_out_$$ 2>&1
if [ -s /tmp/cmd_out_$$ ]; then
cat /tmp/cmd_out_$$ | head -n 10; echo ""
fi
rm -f /tmp/cmd_out_$$
fi

echo -e "${GREEN}✅ 效果: ${effect}${NC}"
echo -e "${CYAN}======================================================${NC}"

# 第二层询问:下一步 或 序号跳转
read -p "$(echo -e "${PURPLE}>> [Enter] 下一步 | [输入序号] 随时跳转 | [q] 退出模拟: ${NC}")" jump_input < /dev/tty

if [[ "$jump_input" == "q" || "$jump_input" == "Q" ]]; then
break
elif [[ "$jump_input" =~ ^[0-9]+$ ]]; then
if [ "$jump_input" -ge 1 ] && [ "$jump_input" -le "$TOTAL_CMDS" ]; then
current_id=$jump_input
else
echo -e "${RED}⚠️ 序号不存在,继续下一题...${NC}"; sleep 1
((current_id++))
fi
else
((current_id++))
fi
done
}

# ======================================================
# 主循环菜单
# ======================================================
while true; do
clear
echo -e "${CYAN}======================================================${NC}"
echo -e "${GREEN} Linux 核心全栈特训营 (v3.0 交互版) ${NC}"
echo -e "${CYAN}======================================================${NC}"
echo -e " 1. 🔍 搜索命令 (输入代码查用法)"
echo -e " 2. 📖 搜索场景 (输入需求查代码)"
echo -e " 3. 📚 全景字典 (带序号完整浏览)"
echo -e " 4. 🚀 进入沙盒模拟实战 (可按序号跳转)"
echo -e " 0. ❌ 退出系统清理环境"
echo -e "${CYAN}======================================================${NC}"
read -p ">> 请选择对应功能序号: " menu_choice < /dev/tty

case $menu_choice in
1) search_cmd ;;
2) search_scene ;;
3) list_all ;;
4) run_simulation ;;
0) cleanup ;;
*) echo -e "${RED}输入有误,请重新选择!${NC}"; sleep 1 ;;
esac
done