forked from admin/linux-practice
Compare commits
16 Commits
5686831d9a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5aafbe8b | ||
|
|
f61376aa8a | ||
|
|
83831b8622 | ||
|
|
9effdee625 | ||
|
|
3f8d4c0ce6 | ||
|
|
3cce199fbf | ||
|
|
a07409af64 | ||
|
|
2c407efbda | ||
|
|
8880f7d383 | ||
|
|
1e9b3a45c5 | ||
|
|
89d2c3b4a3 | ||
|
|
f3d5f92478 | ||
|
|
99a948953b | ||
|
|
de7631985d | ||
|
|
5ca924cadd | ||
|
|
dacc1cb735 |
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
# Python caches
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# OpenClaw interactive edit backups
|
||||
*.interactive.backup.*
|
||||
330
COURSE.md
330
COURSE.md
@@ -1,147 +1,281 @@
|
||||
# 📚 Linux 命令学习课程体系(入门 → 高手)
|
||||
# Linux 学习平台课程设计(运维全场景重构版)
|
||||
|
||||
> 学习路径:**理论 → 演示 → 沙盒练习 → 测试 → 徽章认证**
|
||||
## 总目标
|
||||
|
||||
这套课程不再只是 Linux 命令入门,也不是答题闯关页,而是:
|
||||
|
||||
> **面向运维强相关场景的 Linux 系统学习平台**
|
||||
|
||||
目标是帮助学习者逐步形成三层能力:
|
||||
1. **命令理解能力**:知道命令在解决什么问题
|
||||
2. **系统认知能力**:知道 Linux 系统是怎么组织和运行的
|
||||
3. **运维场景能力**:知道遇到问题时该用哪组命令排查和处理
|
||||
|
||||
---
|
||||
|
||||
## 🌱 Level 1:入门(新手村)
|
||||
## 设计原则
|
||||
|
||||
### 🎯 目标:熟悉终端、查看文件、当前目录、简单操作
|
||||
|
||||
| 课时 | 主题 | 学习内容 | 沙盒练习目标 | 测试场景 |
|
||||
|------|------|---------|-------------|---------|
|
||||
| 1 | `pwd` - 我在哪? | 当前工作目录 | `pwd` 返回 `/sandbox` | 问:你现在的位置是? |
|
||||
| 2 | `ls` - 看看周围 | 列出目录内容 | `ls` 显示 `users projects logs` | 找出 `/sandbox` 下有几个子目录? |
|
||||
| 3 | `cd` - 移动位置 | 切换目录 | `cd users` → 进入用户区 | 从 `/sandbox` 进到 `/projects` |
|
||||
| 4 | `echo` - 说话 | 打印文本 | `echo "Hello Linux"` | 打印你的名字 |
|
||||
| 5 | `cat` - 看内容 | 查看文件内容 | `cat users/alice.txt` | 读出 alice.txt 的内容 |
|
||||
|
||||
**徽章**:` beginner_1 ← 目录旅行者`
|
||||
- **先理解,再操作**
|
||||
- **先场景,再命令**
|
||||
- **先最小可用,再扩展参数**
|
||||
- **练习服务于理解,不反客为主**
|
||||
- **从经典 Linux / Unix 教材与运维认证体系中吸收结构感**
|
||||
- **尽量覆盖运维强相关全场景**
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Level 2:文件操作(手艺人)
|
||||
## 课程组织方式
|
||||
|
||||
### 🎯 目标:创建、复制、移动、删除(安全版)、查看
|
||||
课程采用三层结构:
|
||||
|
||||
| 课时 | 主题 | 学习内容 | 沙盒练习目标 | 测试场景 |
|
||||
|------|------|---------|-------------|---------|
|
||||
| 6 | `touch` - 创建空文件 | 创建新文件 | `touch mynote.txt` | 创建一个名为 `test.txt` 的文件 |
|
||||
| 7 | `cp` - 复制 | 复制文件/目录 | `cp users/alice.txt backup/` | 复制 `project/backend/app.py` 到 `archive/` |
|
||||
| 8 | `mv` - 移动/重命名 | 移动或重命名 | `mv old.txt new.txt` | 把 `logs/access.log` 重命名为 `old_access.log` |
|
||||
| 9 | `mkdir` - 创建目录 | 创建级联目录 | `mkdir -p a/b/c` | 创建 `myproject/src/main` |
|
||||
| 10 | `head/tail` - 看头尾 | 查看文件前/后几行 | `tail -n 5 logs/access.log` | 查看 `app.py` 最后 3 行 |
|
||||
### 第一层:知识模块
|
||||
按 Linux/运维核心领域拆分模块。
|
||||
|
||||
**徽章**:` beginner_2 ← 文件管理员`
|
||||
### 第二层:课时
|
||||
每个课时围绕一个核心问题或一组密切相关命令展开。
|
||||
|
||||
### 第三层:练习
|
||||
练习分为:
|
||||
- 理解题
|
||||
- 操作题
|
||||
- 场景题
|
||||
|
||||
练习是为了巩固,不再主导整个平台体验。
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Level 3:搜索高手(情报员)
|
||||
## 完整课程蓝图(10 大模块)
|
||||
|
||||
### 🎯 目标:快速定位、查找、筛选内容
|
||||
### 模块 1:建立 Linux 基本认知
|
||||
目标:搞清楚终端、路径、目录、文件的基本世界观。
|
||||
|
||||
| 课时 | 主题 | 学习内容 | 沙盒练习目标 | 测试场景 |
|
||||
|------|------|---------|-------------|---------|
|
||||
| 11 | `grep` - 搜索关键词 | 正则匹配文本 | `grep "Linux" *.txt` | 在 `users/` 下找包含 "Alice" 的文件 |
|
||||
| 12 | `find` - 按条件找文件 | 时间/大小/类型 | `find /logs -type f -name "*.log"` | 找出所有 `.py` 文件 |
|
||||
| 13 | `du` - 查看占用 | 磁盘使用情况 | `du -sh *` | 评估 `/projects` 每个子目录大小 |
|
||||
| 14 | `sort` - 排序 | 排序输出 | `ls \| sort` | 按文件大小升序排列 `/logs` |
|
||||
| 15 | `wc` - 统计 | 行/词/字节数 | `wc -l app.py` | `find /projects -type f \| wc -l` 有几个文件? |
|
||||
包含:
|
||||
- 终端与 Shell
|
||||
- 绝对路径 / 相对路径
|
||||
- pwd / ls / cd / cat / echo
|
||||
|
||||
**徽章**:` intermediate_1 ← 情报专家`
|
||||
输出能力:
|
||||
- 能定位自己
|
||||
- 能看懂基本路径
|
||||
- 能读基础文件内容
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Level 4:文本编辑(文字工作者)
|
||||
### 模块 2:文件与目录操作
|
||||
目标:建立文件系统操作能力。
|
||||
|
||||
### 🎯 目标:预览/编辑文本(只读模式)
|
||||
包含:
|
||||
- 文件和目录的区别
|
||||
- mkdir / touch / cp / mv / rm / stat
|
||||
- 文件生命周期:创建、备份、迁移、清理
|
||||
|
||||
| 课时 | 主题 | 学习内容 | 沙盒练习目标 | 测试场景 |
|
||||
|------|------|---------|-------------|---------|
|
||||
| 16 | `nano/vim` 基础 | 只读模式(演示) | `view project/backend/app.py` | 显示文件内容(用 `cat` 替代 vim) |
|
||||
| 17 | `>`/`>>` 重定向 | 输出到文件 | `echo "test" > test.txt` | 把 `grep "def" app.py` 结果保存到 `methods.txt` |
|
||||
| 18 | `|` 管道 | 连接命令 | `cat users/* \| grep Alice` | 找出所有包含 "Linux" 的内容 |
|
||||
|
||||
**徽章**:` intermediate_2 ← 文字工匠`
|
||||
输出能力:
|
||||
- 能完成基础文件管理
|
||||
- 能理解文件操作的风险和意义
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Level 5:高级技巧(小黑客)
|
||||
### 模块 3:阅读与筛选信息
|
||||
目标:建立日志、配置、文本信息处理能力。
|
||||
|
||||
### 🎯 目标:权限、查找大文件、进程、自动化
|
||||
包含:
|
||||
- head / tail / grep / wc / sort / find
|
||||
- 搜索与筛选思路
|
||||
- 日志阅读思路
|
||||
|
||||
| 课时 | 主题 | 学习内容 | 沙盒练习目标 | 测试场景 |
|
||||
|------|------|---------|-------------|---------|
|
||||
| 19 | `find -size` - 找大文件 | 按大小查找 | `find / -size +100M` | 找出 `/logs` 下大于 10KB 的文件 |
|
||||
| 20 | `chmod` 原理 | 权限概念(只读) | 解释 `rwxr-xr-x` | 问:`644` 是什么权限? |
|
||||
| 21 | `ps`/`top` 基础 | 进程概念 | `ps aux \| grep python` | 找出所有 `java` 进程 |
|
||||
| 22 | `history` - 命令历史 | 查看历史 | `history \| tail -n 10` | 看最近 3 条执行的命令 |
|
||||
| 23 | `man` - 查手册 | 查帮助 | `man ls`(模拟) | 问:`ls -a` 是什么作用? |
|
||||
|
||||
**徽章**:` advanced_1 ← 系统法师`
|
||||
输出能力:
|
||||
- 能读日志
|
||||
- 能找关键词
|
||||
- 能定位文件
|
||||
- 能做基础统计
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Level 6:实战项目(通关玩家)
|
||||
### 模块 4:系统状态与资源认知
|
||||
目标:知道一台 Linux 机器此刻在运行什么。
|
||||
|
||||
### 🎯 综合应用:解决真实场景
|
||||
包含:
|
||||
- 进程、负载、CPU、内存、磁盘、挂载点
|
||||
- ps / top / uptime / free / df / du / mount / lsof
|
||||
|
||||
| 场景 | 任务 | 所需命令 | 难度 |
|
||||
|------|------|----------|------|
|
||||
| 📁 备份项目 | 将 `/projects` 下所有 `.py` 文件备份到 `/backup` | `find`, `cp`, `mkdir` | ⚔️⚔️⚔️ |
|
||||
| 🔎 搜索日志 | 找出所有包含 `"error"` 的日志行 | `grep`, `find`, `cat` | ⚔️⚔️⚔️⚔️ |
|
||||
| 📏 磁盘分析 | 写出 `/projects` 中最大的 3 个文件 | `du`, `sort`, `tail` | ⚔️⚔️ |
|
||||
| 📝 生成报告 | 把所有 `.py` 文件的行数统计保存到 `stats.txt` | `wc`, `find`, `>` | ⚔️⚔️⚔️⚔️⚔️ |
|
||||
| 🔐 权限检查 | 找出所有`.sh` 脚本并检查权限是否为 `755` | `find`, `stat`, `grep` | ⚔️⚔️⚔️⚔️⚔️ |
|
||||
输出能力:
|
||||
- 能做基础资源排查
|
||||
- 能理解系统是否正常运行
|
||||
|
||||
---
|
||||
|
||||
## 🏆 完整通关徽章体系
|
||||
### 模块 5:服务与日志排障
|
||||
目标:围绕服务故障建立排查链路。
|
||||
|
||||
```bash
|
||||
beginner_1 ← 目录旅行者 (pwd/ls/cd)
|
||||
beginner_2 ← 文件管理员 (touch/cp/mv/mkdir)
|
||||
intermediate_1 ← 情报专家 (grep/find/du/sort/wc)
|
||||
intermediate_2 ← 文字工匠 (cat/echo/pipe/redirection)
|
||||
advanced_1 ← 系统法师 (find-size/chmod/ps/history/man)
|
||||
expert_1 ← 实战大师 (综合项目通关)
|
||||
legend ← Linux 大师 (所有课程 + 心得分享)
|
||||
```
|
||||
包含:
|
||||
- systemd 基础
|
||||
- 服务状态 / 启停 / 自启动
|
||||
- journalctl
|
||||
- 进程、端口、日志之间的关系
|
||||
- systemctl / service / journalctl / kill / pkill / nohup
|
||||
|
||||
输出能力:
|
||||
- 能处理“服务没起来 / 起了但不可用 / 日志报错”类问题
|
||||
|
||||
---
|
||||
|
||||
## 📝 测验模式设计
|
||||
### 模块 6:网络与连接排查
|
||||
目标:建立网络、监听、请求和服务可达性的认知。
|
||||
|
||||
每个课时结束后,自动弹出:
|
||||
包含:
|
||||
- 网卡 / IP / 端口 / 监听 / 连通性
|
||||
- ifconfig / ip addr / ping / ss / netstat / curl / wget / traceroute / dig
|
||||
|
||||
```bash
|
||||
🎯 当前任务:_____________
|
||||
💡 提示:_________________
|
||||
(stdin) > [输入命令]
|
||||
|
||||
✅ 回答正确!获得经验值 +100
|
||||
❌ 还未达标!提示:试试 `xxx`
|
||||
```
|
||||
|
||||
答对 3 次 → 解锁下一关
|
||||
输出能力:
|
||||
- 能判断服务通不通
|
||||
- 能区分网络层、端口层、HTTP 层的问题
|
||||
|
||||
---
|
||||
|
||||
## 🎯 课程特色
|
||||
### 模块 7:权限、用户与安全基础
|
||||
目标:从“能执行”提升到“知道该不该执行”。
|
||||
|
||||
- ✅ **零风险沙盒**:所有命令在虚拟环境中执行,不会影响真实系统
|
||||
- ✅ **闯关式学习**:从入门到高手,逐步解锁新技能
|
||||
- ✅ **即时反馈**:答对/错都有针对性提示
|
||||
- ✅ **实战导向**:每个级别都有真实业务场景
|
||||
- ✅ **徽章认证**:每完成一个阶段获得专属徽章
|
||||
包含:
|
||||
- 用户、组、权限模型
|
||||
- chmod / chown / chgrp / whoami / id / passwd / sudo / su
|
||||
- 最小权限原则
|
||||
- 高风险命令意识
|
||||
|
||||
输出能力:
|
||||
- 能处理基础权限问题
|
||||
- 能理解安全边界
|
||||
|
||||
---
|
||||
|
||||
需要我根据这个课程体系开始实现吗?包括:
|
||||
1. `COURSE_TASKS.json`(所有练习题)
|
||||
2. 沙盒模拟器 `sandbox.py`
|
||||
3. 熟悉 `server.py` 重构
|
||||
4. UI 改造(闯关式界面)
|
||||
5. `README.md` 使用文档
|
||||
### 模块 8:软件包、环境与命令定位
|
||||
目标:理解软件从哪里来、命令为什么能执行、版本怎么查。
|
||||
|
||||
还是先调整下课程内容?😄
|
||||
包含:
|
||||
- PATH 与命令查找
|
||||
- which / whereis / env / export / alias
|
||||
- apt / dpkg / yum / rpm
|
||||
|
||||
输出能力:
|
||||
- 能定位命令来源
|
||||
- 能查版本、查安装包、查环境变量问题
|
||||
|
||||
---
|
||||
|
||||
### 模块 9:自动化、归档与运维习惯
|
||||
目标:建立“批量处理、定时执行、可重复操作”的意识。
|
||||
|
||||
包含:
|
||||
- 重定向 / 管道
|
||||
- crontab
|
||||
- tar / gzip / zip / unzip
|
||||
- history
|
||||
- Shell 习惯
|
||||
|
||||
输出能力:
|
||||
- 能做基础自动化
|
||||
- 能做备份和简单定时任务
|
||||
- 能形成更稳妥的命令行习惯
|
||||
|
||||
---
|
||||
|
||||
### 模块 10:运维综合实战场景
|
||||
目标:把前面所有能力真正串起来。
|
||||
|
||||
典型场景:
|
||||
- 服务无法访问排查
|
||||
- 磁盘爆满排查
|
||||
- 登录失败排查
|
||||
- Nginx / 应用日志排查
|
||||
- 发布后服务未启动
|
||||
- 备份与恢复演练
|
||||
|
||||
输出能力:
|
||||
- 知道不是背命令,而是围绕问题组织命令链路
|
||||
|
||||
---
|
||||
|
||||
## 每课统一结构
|
||||
|
||||
每个课时都按下列结构组织:
|
||||
|
||||
1. **学什么**
|
||||
2. **为什么重要**
|
||||
3. **核心知识点**
|
||||
4. **最小示例**
|
||||
5. **常见误区**
|
||||
6. **典型场景**
|
||||
7. **练习题**
|
||||
8. **课后总结 / 迁移建议**(后续补齐)
|
||||
|
||||
---
|
||||
|
||||
## 练习设计原则
|
||||
|
||||
### A. 理解题
|
||||
检查有没有理解命令用途和思路。
|
||||
|
||||
### B. 操作题
|
||||
检查是否能写出正确命令。
|
||||
|
||||
### C. 场景题
|
||||
检查是否能把命令放进真实运维问题中。
|
||||
|
||||
> 练习不是为了“刷过去”,而是为了确认:你是不是真的知道这个命令为什么存在。
|
||||
|
||||
---
|
||||
|
||||
## 页面结构原则
|
||||
|
||||
### 左侧:课程地图
|
||||
- 模块
|
||||
- 课时
|
||||
- 学习路径
|
||||
|
||||
### 中间:正文学习区
|
||||
- 讲解优先
|
||||
- 示例优先
|
||||
- 场景优先
|
||||
|
||||
### 右侧:辅助理解区
|
||||
- 易错点
|
||||
- 相关命令
|
||||
- 场景提示
|
||||
- 理解型问题
|
||||
|
||||
### 底部 / 内嵌:轻练习区
|
||||
- 练习服务于学习
|
||||
- 保持必要但不过分喧宾夺主
|
||||
|
||||
---
|
||||
|
||||
## 吸收的“教材气质”
|
||||
|
||||
这套平台后续会持续内化这些来源的设计方法:
|
||||
- 经典 Linux / Unix 入门教材的结构感
|
||||
- man / 官方文档的准确性
|
||||
- RHCSA / RHCE / LPIC 一类认证体系的能力递进
|
||||
- 真实运维工作流里的问题链路
|
||||
|
||||
不是搬教材,而是:
|
||||
|
||||
> **把经典教材与运维经验内化后,重新设计成适合平台学习的课程。**
|
||||
|
||||
---
|
||||
|
||||
## 当前重构阶段
|
||||
|
||||
### 已完成
|
||||
- 平台方向从交互优先改成学习优先
|
||||
- 前 3 个模块已落到课程结构中
|
||||
- 页面结构已切为知识正文型
|
||||
|
||||
### 下一步
|
||||
- 扩完整个 10 模块蓝图到课程数据
|
||||
- 逐步补模块 4~10 的详细课时
|
||||
- 增加“场景专题”页,把命令真正串成运维链路
|
||||
|
||||
---
|
||||
|
||||
## 一句话定位
|
||||
|
||||
> **这是一个吸收经典 Linux 教材与运维训练体系后,面向运维强相关全场景重构的 Linux 学习平台。**
|
||||
|
||||
@@ -1,191 +1,585 @@
|
||||
{
|
||||
"meta": {
|
||||
"version": "3.0",
|
||||
"title": "Linux 运维工程师完整教程",
|
||||
"author": "PMClaw",
|
||||
"updated": "2026-03-06",
|
||||
"description": "覆盖菜鸟教程 Linux 全部内容,从入门到精通",
|
||||
"total_levels": 12,
|
||||
"total_challenges": 80
|
||||
"version": "5.1",
|
||||
"title": "Linux Operations Learning Lab",
|
||||
"author": "Codex",
|
||||
"updated": "2026-03-19",
|
||||
"description": "A rebuilt Linux learning path that moves from command basics to real operations troubleshooting with sandbox exercises.",
|
||||
"module_count": 8,
|
||||
"total_lessons": 24,
|
||||
"total_exercises": 82,
|
||||
"pedagogy": "learning-first",
|
||||
"orientation": "ops-workflow"
|
||||
},
|
||||
"levels": [
|
||||
"modules": [
|
||||
{
|
||||
"id": "level_1_basic",
|
||||
"title": "🌱 Level 1: Linux 基础入门",
|
||||
"description": "Linux 简介、目录结构、基本操作",
|
||||
"challenges": [
|
||||
{"id": "l1_1_pwd", "title": "查看当前目录", "description": "使用 pwd 命令显示当前工作目录的完整路径", "hint": "直接输入 pwd", "success_test": "'/' in output", "solution": ["pwd"], "success_msg": "📍 定位成功!"},
|
||||
{"id": "l1_2_ls", "title": "列出目录内容", "description": "使用 ls 命令查看当前目录下的文件和文件夹", "hint": "输入 ls", "success_test": "len(output) > 0", "solution": ["ls"], "success_msg": "📂 目录内容已显示!"},
|
||||
{"id": "l1_3_ls_l", "title": "详细列表", "description": "使用 ls -l 显示详细信息,包括权限、所有者、大小等", "hint": "ls -l", "success_test": "'total' in output or '-' in output[:20]", "solution": ["ls -l"], "success_msg": "📋 详细信息已获取!"},
|
||||
{"id": "l1_4_ls_a", "title": "显示隐藏文件", "description": "使用 ls -a 显示包括隐藏文件在内的所有文件", "hint": "ls -a", "success_test": "'.' in output and '..' in output", "solution": ["ls -a"], "success_msg": "👻 隐藏文件已显示!"},
|
||||
{"id": "l1_5_cd", "title": "切换目录", "description": "使用 cd 命令进入 /tmp 目录", "hint": "cd /tmp", "success_test": "cwd == '/tmp'", "solution": ["cd /tmp"], "success_msg": "🚶 目录切换成功!"},
|
||||
{"id": "l1_6_cd_back", "title": "返回上级目录", "description": "使用 cd .. 返回上级目录", "hint": "cd ..", "success_test": "cwd != '/tmp'", "solution": ["cd .."], "success_msg": "⬆️ 返回成功!"},
|
||||
{"id": "l1_7_cd_home", "title": "返回用户主目录", "description": "使用 cd ~ 或 cd 返回用户主目录", "hint": "cd ~", "success_test": "'home' in cwd or cwd == '/'", "solution": ["cd ~", "cd"], "success_msg": "🏠 已回到家目录!"},
|
||||
{"id": "l1_8_clear", "title": "清屏", "description": "使用 clear 命令清屏", "hint": "clear", "success_test": "cmd == 'clear'", "solution": ["clear"], "success_msg": "🧹 屏幕已清空!"}
|
||||
"id": "module_1_foundation",
|
||||
"title": "Module 1: Build Linux spatial awareness",
|
||||
"summary": "Start with location and listing so every later action has context.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m1_l1_pwd",
|
||||
"title": "Locate yourself with pwd",
|
||||
"goal": "Understand the current working directory before changing files or running scripts.",
|
||||
"why_it_matters": "Many Linux mistakes come from running the right command in the wrong path.",
|
||||
"concepts": ["Current working directory", "Absolute vs relative path", "Context before action"],
|
||||
"command": "pwd",
|
||||
"examples": ["pwd", "cd /tmp && pwd"],
|
||||
"pitfalls": ["Assuming every shell opens in the same place", "Running a file command before checking the path"],
|
||||
"scenarios": ["Verify where you are before editing a file", "Confirm location before launching a script"],
|
||||
"troubleshooting_flow": ["Confirm the working context", "Run pwd", "Use the output to choose the next command"],
|
||||
"related_commands": ["pwd", "cd", "ls"],
|
||||
"classic_view": "Treat pwd as the command that restores orientation before every other action.",
|
||||
"takeaways": ["Know your location before acting", "Read the full path instead of guessing", "Use location to decide the next safe step"],
|
||||
"after_class": "Repeat pwd before every directory-changing command until it becomes automatic.",
|
||||
"exercises": [
|
||||
{ "id": "m1_l1_e1", "type": "understanding", "question": "Which command prints the full current working directory?", "answer": "pwd" },
|
||||
{ "id": "m1_l1_e2", "type": "operation", "title": "Print the current working directory", "hint": "Run pwd directly.", "success_test": "cmd == 'pwd'", "solution": ["pwd"], "success_msg": "You verified the shell location correctly." },
|
||||
{ "id": "m1_l1_e3", "type": "scenario", "question": "If a script fails with 'file not found', what should you check first?", "answer": "Run pwd and verify the current path before anything else." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m1_l2_ls",
|
||||
"title": "Inspect a directory with ls",
|
||||
"goal": "Observe directory content and switch to detail views when needed.",
|
||||
"why_it_matters": "Linux exploration usually begins with ls because you need to know what exists before you inspect more deeply.",
|
||||
"concepts": ["Directory contents", "Hidden files", "Long listing output"],
|
||||
"command": "ls",
|
||||
"examples": ["ls", "ls -la", "ls -l /etc"],
|
||||
"pitfalls": ["Thinking a hidden file does not exist because plain ls missed it", "Reading long output without understanding permissions or size"],
|
||||
"scenarios": ["Check what files exist before deleting anything", "Inspect a config directory for hidden dotfiles"],
|
||||
"troubleshooting_flow": ["List the directory first", "Switch to ls -la if details matter", "Use the listing to choose the next file command"],
|
||||
"related_commands": ["ls", "find", "stat"],
|
||||
"classic_view": "Observation comes before diagnosis, and ls is usually the first observation tool.",
|
||||
"takeaways": ["Use ls for broad visibility", "Use ls -la for hidden files and metadata", "Treat the listing as evidence"],
|
||||
"after_class": "Practice comparing ls and ls -la until you can explain why the outputs differ.",
|
||||
"exercises": [
|
||||
{ "id": "m1_l2_e1", "type": "understanding", "question": "Why does ls -a reveal more than plain ls?", "answer": "Because it includes hidden dotfiles." },
|
||||
{ "id": "m1_l2_e2", "type": "operation", "title": "List the current directory", "hint": "Start with ls.", "success_test": "cmd == 'ls'", "solution": ["ls"], "success_msg": "You listed the current directory." },
|
||||
{ "id": "m1_l2_e3", "type": "operation", "title": "Show hidden files and details", "hint": "Use ls -la.", "success_test": "cmd == 'ls -la' or cmd == 'ls -al'", "solution": ["ls -la", "ls -al"], "success_msg": "You switched from a simple listing to a full inspection view." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m1_l3_cd_cat_echo",
|
||||
"title": "Move, read, and confirm with cd, cat, and echo",
|
||||
"goal": "Use navigation, file reads, and quick output together as one everyday workflow.",
|
||||
"why_it_matters": "Many Linux tasks are just a chain of navigate, inspect, and confirm.",
|
||||
"concepts": ["Changing directories", "Reading text files", "Using echo for quick verification"],
|
||||
"command": "cd / cat / echo",
|
||||
"examples": ["cd /tmp", "cat /etc/hosts", "echo Hello Linux"],
|
||||
"pitfalls": ["Using cat on a directory", "Changing directories without verifying where you landed", "Ignoring echo as a debugging aid"],
|
||||
"scenarios": ["Enter a workspace and inspect a config file", "Print quick checkpoints in a shell session"],
|
||||
"troubleshooting_flow": ["Change to the right location", "Read the right file", "Echo the key assumption you want to verify"],
|
||||
"related_commands": ["cd", "cat", "echo", "pwd"],
|
||||
"classic_view": "These commands make context and state visible before you do more powerful operations.",
|
||||
"takeaways": ["Use cd intentionally", "Use cat for quick text inspection", "Use echo to make invisible state explicit"],
|
||||
"after_class": "Practice saying out loud what each command changed in your working context.",
|
||||
"exercises": [
|
||||
{ "id": "m1_l3_e1", "type": "operation", "title": "Enter /tmp", "hint": "Use cd /tmp.", "success_test": "cmd == 'cd /tmp' and cwd == '/tmp'", "solution": ["cd /tmp"], "success_msg": "You moved to the target working directory." },
|
||||
{ "id": "m1_l3_e2", "type": "operation", "title": "Read the hosts file", "hint": "Run cat /etc/hosts.", "success_test": "cmd == 'cat /etc/hosts' and 'localhost' in output", "solution": ["cat /etc/hosts"], "success_msg": "You inspected a real text file in the sandbox." },
|
||||
{ "id": "m1_l3_e3", "type": "operation", "title": "Print a confirmation message", "hint": "Run echo Hello Linux.", "success_test": "cmd == 'echo Hello Linux' and 'Hello Linux' in output", "solution": ["echo Hello Linux"], "success_msg": "You used echo to make the current step explicit." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_2_file",
|
||||
"title": "📁 Level 2: 文件与目录管理",
|
||||
"description": "创建、删除、复制、移动文件和目录",
|
||||
"challenges": [
|
||||
{"id": "l2_1_mkdir", "title": "创建目录", "description": "使用 mkdir 创建 /tmp/testdir 目录", "hint": "mkdir /tmp/testdir", "success_test": "exists('/tmp/testdir')", "solution": ["mkdir /tmp/testdir"], "success_msg": "📂 目录创建成功!"},
|
||||
{"id": "l2_2_mkdir_p", "title": "递归创建目录", "description": "使用 mkdir -p 创建 /tmp/a/b/c 多级目录", "hint": "mkdir -p /tmp/a/b/c", "success_test": "exists('/tmp/a/b/c')", "solution": ["mkdir -p /tmp/a/b/c"], "success_msg": "📁 多级目录创建成功!"},
|
||||
{"id": "l2_3_touch", "title": "创建空文件", "description": "使用 touch 创建 /tmp/test.txt 文件", "hint": "touch /tmp/test.txt", "success_test": "exists('/tmp/test.txt')", "solution": ["touch /tmp/test.txt"], "success_msg": "📄 文件创建成功!"},
|
||||
{"id": "l2_4_cp", "title": "复制文件", "description": "将 /etc/hosts 复制到 /tmp/hosts.bak", "hint": "cp /etc/hosts /tmp/hosts.bak", "success_test": "exists('/tmp/hosts.bak')", "solution": ["cp /etc/hosts /tmp/hosts.bak"], "success_msg": "📋 文件复制成功!"},
|
||||
{"id": "l2_5_cp_r", "title": "复制目录", "description": "使用 cp -r 复制 /etc/skel 到 /tmp/skel_backup", "hint": "cp -r /etc/skel /tmp/skel_backup", "success_test": "exists('/tmp/skel_backup')", "solution": ["cp -r /etc/skel /tmp/skel_backup"], "success_msg": "📁 目录复制成功!"},
|
||||
{"id": "l2_6_mv", "title": "移动文件", "description": "将 /tmp/test.txt 移动到 /tmp/testdir/", "hint": "mv /tmp/test.txt /tmp/testdir/", "success_test": "exists('/tmp/testdir/test.txt')", "solution": ["mv /tmp/test.txt /tmp/testdir/"], "success_msg": "🚚 文件移动成功!"},
|
||||
{"id": "l2_7_mv_rename", "title": "重命名文件", "description": "将 /tmp/hosts.bak 重命名为 /tmp/hosts.backup", "hint": "mv /tmp/hosts.bak /tmp/hosts.backup", "success_test": "exists('/tmp/hosts.backup')", "solution": ["mv /tmp/hosts.bak /tmp/hosts.backup"], "success_msg": "✏️ 重命名成功!"},
|
||||
{"id": "l2_8_rm", "title": "删除文件", "description": "删除 /tmp/hosts.backup 文件", "hint": "rm /tmp/hosts.backup", "success_test": "not exists('/tmp/hosts.backup')", "solution": ["rm /tmp/hosts.backup"], "success_msg": "🗑️ 文件删除成功!"},
|
||||
{"id": "l2_9_rm_r", "title": "删除目录", "description": "使用 rm -r 删除 /tmp/skel_backup 目录", "hint": "rm -r /tmp/skel_backup", "success_test": "not exists('/tmp/skel_backup')", "solution": ["rm -r /tmp/skel_backup"], "success_msg": "🗑️ 目录删除成功!"},
|
||||
{"id": "l2_10_rm_rf", "title": "强制删除", "description": "使用 rm -rf 强制删除 /tmp/a 目录及其所有内容", "hint": "rm -rf /tmp/a", "success_test": "not exists('/tmp/a')", "solution": ["rm -rf /tmp/a"], "success_msg": "💥 强制删除成功!"}
|
||||
"id": "module_2_filesystem",
|
||||
"title": "Module 2: Manipulate files safely",
|
||||
"summary": "Learn to create, copy, move, and permission files without losing context.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m2_l1_create",
|
||||
"title": "Create structure with mkdir and touch",
|
||||
"goal": "Build directories and empty files quickly so you can prepare workspaces or examples.",
|
||||
"why_it_matters": "Project setup and environment preparation often begin with directory scaffolding.",
|
||||
"concepts": ["Directory creation", "Recursive directory creation", "Empty file creation"],
|
||||
"command": "mkdir / touch",
|
||||
"examples": ["mkdir /tmp/lab", "mkdir -p /tmp/lab/data/raw", "touch /tmp/lab/notes.txt"],
|
||||
"pitfalls": ["Forgetting -p for nested paths", "Trying to touch a file under a missing directory"],
|
||||
"scenarios": ["Prepare a clean lab directory", "Create a placeholder log or notes file"],
|
||||
"troubleshooting_flow": ["Check the target path", "Create directories first", "Create files after the path exists"],
|
||||
"related_commands": ["mkdir", "touch", "ls"],
|
||||
"classic_view": "File-system work is safer when you change one layer at a time: path first, file second.",
|
||||
"takeaways": ["Use mkdir for structure", "Use touch for placeholders", "Think about parent directories before file creation"],
|
||||
"after_class": "Rebuild the same directory tree twice until the path feels natural.",
|
||||
"exercises": [
|
||||
{ "id": "m2_l1_e1", "type": "operation", "title": "Create a lab directory", "hint": "Run mkdir /tmp/lab.", "success_test": "cmd == 'mkdir /tmp/lab' and exists('/tmp/lab')", "solution": ["mkdir /tmp/lab"], "success_msg": "Your lab directory now exists." },
|
||||
{ "id": "m2_l1_e2", "type": "operation", "title": "Create a nested directory tree", "hint": "Run mkdir -p /tmp/lab/data/raw.", "success_test": "cmd == 'mkdir -p /tmp/lab/data/raw' and exists('/tmp/lab/data/raw')", "solution": ["mkdir -p /tmp/lab/data/raw"], "success_msg": "You created the nested path correctly." },
|
||||
{ "id": "m2_l1_e3", "type": "operation", "title": "Create a note file", "hint": "Run touch /tmp/lab/notes.txt.", "success_test": "cmd == 'touch /tmp/lab/notes.txt' and exists('/tmp/lab/notes.txt')", "solution": ["touch /tmp/lab/notes.txt"], "success_msg": "You created a new empty file in the lab path." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m2_l2_copy_move_permission",
|
||||
"title": "Copy, move, and permission files with intent",
|
||||
"goal": "Manipulate files carefully and connect permission changes to execution behavior.",
|
||||
"why_it_matters": "Safe Linux work depends on knowing when you are backing up, renaming, or changing access.",
|
||||
"concepts": ["Backup copies", "Rename vs move", "Permission modes"],
|
||||
"command": "cp / mv / chmod / stat",
|
||||
"examples": ["cp /etc/hosts /tmp/hosts.bak", "mv /tmp/hosts.bak /tmp/hosts.backup", "chmod 755 /tmp/lab", "stat /etc/hosts"],
|
||||
"pitfalls": ["Deleting before backing up", "Changing permissions without checking metadata first"],
|
||||
"scenarios": ["Back up a config before editing", "Make a script or directory executable when needed"],
|
||||
"troubleshooting_flow": ["Copy before editing", "Rename with a purpose", "Inspect metadata before permission changes"],
|
||||
"related_commands": ["cp", "mv", "chmod", "stat"],
|
||||
"classic_view": "File operations are safe when you can clearly name the step: backup, rename, inspect, or permission change.",
|
||||
"takeaways": ["Back up first", "Rename with purpose", "Inspect before changing permissions"],
|
||||
"after_class": "Repeat the flow copy -> rename -> stat -> chmod on a temporary file until the sequence feels obvious.",
|
||||
"exercises": [
|
||||
{ "id": "m2_l2_e1", "type": "operation", "title": "Back up the hosts file", "hint": "Run cp /etc/hosts /tmp/hosts.bak.", "success_test": "cmd == 'cp /etc/hosts /tmp/hosts.bak' and exists('/tmp/hosts.bak')", "solution": ["cp /etc/hosts /tmp/hosts.bak"], "success_msg": "You created a backup copy before any change." },
|
||||
{ "id": "m2_l2_e2", "type": "operation", "title": "Rename the backup file", "hint": "Run mv /tmp/hosts.bak /tmp/hosts.backup.", "success_test": "cmd == 'mv /tmp/hosts.bak /tmp/hosts.backup' and exists('/tmp/hosts.backup')", "solution": ["mv /tmp/hosts.bak /tmp/hosts.backup"], "success_msg": "You renamed the file successfully." },
|
||||
{ "id": "m2_l2_e3", "type": "operation", "title": "Inspect hosts metadata", "hint": "Run stat /etc/hosts.", "success_test": "cmd == 'stat /etc/hosts' and 'File:' in output", "solution": ["stat /etc/hosts"], "success_msg": "You inspected the file metadata successfully." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m2_l3_cleanup_and_modes",
|
||||
"title": "Clean up and change modes with rm and chmod",
|
||||
"goal": "Delete temporary artifacts safely and connect permission changes to execution behavior.",
|
||||
"why_it_matters": "Cleanup and permission changes are common, but both can create damage if you skip inspection.",
|
||||
"concepts": ["Cleanup order", "Symbolic vs numeric modes", "Execution permission"],
|
||||
"command": "rm / chmod",
|
||||
"examples": ["rm /tmp/hosts.backup", "chmod 755 /tmp/lab", "chmod +x /tmp/lab/notes.txt"],
|
||||
"pitfalls": ["Deleting before confirming the file is disposable", "Changing permissions without knowing why"],
|
||||
"scenarios": ["Remove temporary files after verification", "Make a script or directory executable when needed"],
|
||||
"troubleshooting_flow": ["Verify the target path", "Remove only temporary files", "Inspect and then change permissions with intent"],
|
||||
"related_commands": ["rm", "chmod", "stat", "ls"],
|
||||
"classic_view": "Cleanup and permission work should be deliberate, not reactive.",
|
||||
"takeaways": ["Delete last", "Use numeric and symbolic modes intentionally", "Connect permissions to a real need"],
|
||||
"after_class": "Repeat the sequence inspect -> remove or inspect -> chmod until it feels like a safe habit.",
|
||||
"exercises": [
|
||||
{ "id": "m2_l3_e1", "type": "operation", "title": "Remove the renamed backup", "hint": "Run rm /tmp/hosts.backup.", "success_test": "cmd == 'rm /tmp/hosts.backup' and not exists('/tmp/hosts.backup')", "solution": ["rm /tmp/hosts.backup"], "success_msg": "You cleaned up the temporary backup safely." },
|
||||
{ "id": "m2_l3_e2", "type": "operation", "title": "Set the lab directory mode to 755", "hint": "Run chmod 755 /tmp/lab.", "success_test": "cmd == 'chmod 755 /tmp/lab'", "solution": ["chmod 755 /tmp/lab"], "success_msg": "You applied a numeric permission change." },
|
||||
{ "id": "m2_l3_e3", "type": "operation", "title": "Add execute permission to notes.txt", "hint": "Run chmod +x /tmp/lab/notes.txt.", "success_test": "cmd == 'chmod +x /tmp/lab/notes.txt'", "solution": ["chmod +x /tmp/lab/notes.txt"], "success_msg": "You used a symbolic permission change." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_3_view",
|
||||
"title": "👁️ Level 3: 文件内容查看",
|
||||
"description": "cat、more、less、head、tail 等查看命令",
|
||||
"challenges": [
|
||||
{"id": "l3_1_cat", "title": "查看文件内容", "description": "使用 cat 查看 /etc/passwd 文件内容", "hint": "cat /etc/passwd", "success_test": "'root' in output", "solution": ["cat /etc/passwd"], "success_msg": "📖 文件内容已显示!"},
|
||||
{"id": "l3_2_cat_n", "title": "显示行号", "description": "使用 cat -n 显示 /etc/passwd 并带行号", "hint": "cat -n /etc/passwd", "success_test": "'1 ' in output or ' 1 ' in output", "solution": ["cat -n /etc/passwd"], "success_msg": "🔢 行号已显示!"},
|
||||
{"id": "l3_3_head", "title": "查看开头", "description": "使用 head 查看 /etc/passwd 前 10 行", "hint": "head /etc/passwd", "success_test": "len(output.split('\\n')) >= 5", "solution": ["head /etc/passwd"], "success_msg": "👆 开头内容已显示!"},
|
||||
{"id": "l3_4_head_n", "title": "指定行数", "description": "使用 head -n 5 查看前 5 行", "hint": "head -n 5 /etc/passwd", "success_test": "len(output.split('\\n')) <= 7", "solution": ["head -5 /etc/passwd", "head -n 5 /etc/passwd"], "success_msg": "📏 指定行数已显示!"},
|
||||
{"id": "l3_5_tail", "title": "查看结尾", "description": "使用 tail 查看 /etc/passwd 最后 10 行", "hint": "tail /etc/passwd", "success_test": "len(output) > 10", "solution": ["tail /etc/passwd"], "success_msg": "👇 结尾内容已显示!"},
|
||||
{"id": "l3_6_tail_f", "title": "实时追踪", "description": "使用 tail -f 实时查看 /var/log/syslog(按 Ctrl+C 退出)", "hint": "tail -f /var/log/syslog", "success_test": "cmd.startswith('tail -f')", "solution": ["tail -f /var/log/syslog"], "success_msg": "👀 实时追踪模式!"},
|
||||
{"id": "l3_7_more", "title": "分页查看", "description": "使用 more 分页查看 /etc/passwd", "hint": "more /etc/passwd", "success_test": "cmd.startswith('more')", "solution": ["more /etc/passwd"], "success_msg": "📄 分页查看模式!"},
|
||||
{"id": "l3_8_less", "title": "可滚动查看", "description": "使用 less 查看 /etc/passwd(支持上下滚动)", "hint": "less /etc/passwd", "success_test": "cmd.startswith('less')", "solution": ["less /etc/passwd"], "success_msg": "📜 可滚动查看模式!"}
|
||||
"id": "module_3_search",
|
||||
"title": "Module 3: Search and preview with evidence",
|
||||
"summary": "Move from a broad search to a precise answer using filters and short file views.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m3_l1_grep",
|
||||
"title": "Search file content with grep",
|
||||
"goal": "Find relevant lines quickly instead of reading an entire file every time.",
|
||||
"why_it_matters": "Operations work often means locating one useful line inside a noisy file.",
|
||||
"concepts": ["Pattern search", "Case-insensitive search", "Line-oriented inspection"],
|
||||
"command": "grep",
|
||||
"examples": ["grep sandbox_user /etc/passwd", "grep -i error /var/log/syslog"],
|
||||
"pitfalls": ["Searching too broadly and ignoring context", "Forgetting case-insensitive mode"],
|
||||
"scenarios": ["Find one user entry in passwd", "Locate error lines in a log"],
|
||||
"troubleshooting_flow": ["Choose the right file first", "Search for the smallest useful keyword", "Use the matched lines to decide the next command"],
|
||||
"related_commands": ["grep", "cat", "tail"],
|
||||
"classic_view": "grep turns a large text space into a small set of signals you can reason about.",
|
||||
"takeaways": ["Search deliberately", "Narrow the signal before changing anything", "Use matched lines as evidence"],
|
||||
"after_class": "Practice moving from a broad keyword to a more precise keyword in the same file.",
|
||||
"exercises": [
|
||||
{ "id": "m3_l1_e1", "type": "operation", "title": "Find sandbox_user in passwd", "hint": "Run grep sandbox_user /etc/passwd.", "success_test": "cmd == 'grep sandbox_user /etc/passwd' and 'sandbox_user' in output", "solution": ["grep sandbox_user /etc/passwd"], "success_msg": "You extracted the relevant passwd line." },
|
||||
{ "id": "m3_l1_e2", "type": "operation", "title": "Find error lines in syslog", "hint": "Run grep -i error /var/log/syslog.", "success_test": "cmd == 'grep -i error /var/log/syslog' and 'error' in output.lower()", "solution": ["grep -i error /var/log/syslog"], "success_msg": "You filtered the log down to the error lines." },
|
||||
{ "id": "m3_l1_e3", "type": "scenario", "question": "Why is grep usually faster than reading the whole log manually?", "answer": "Because it narrows the file to only the lines that match the signal you care about." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m3_l2_find_preview",
|
||||
"title": "Locate files with find and preview them with tail",
|
||||
"goal": "Move from path discovery to a focused log or config preview.",
|
||||
"why_it_matters": "You cannot inspect the right file until you locate it precisely, and you do not need to open the whole file first.",
|
||||
"concepts": ["Name-based search", "Search roots", "Tail as a recent-state preview"],
|
||||
"command": "find / tail",
|
||||
"examples": ["find /etc -name '*.conf'", "find /var/log -type f", "tail -n 1 /var/log/auth.log"],
|
||||
"pitfalls": ["Searching from too high in the tree", "Using cat on a large file when tail would answer faster"],
|
||||
"scenarios": ["Find config files under /etc", "Inspect the newest auth log line"],
|
||||
"troubleshooting_flow": ["Pick the smallest reasonable root", "Add a filter", "Preview the most relevant file segment"],
|
||||
"related_commands": ["find", "tail", "head"],
|
||||
"classic_view": "A good search ends with an exact path and a small, readable preview.",
|
||||
"takeaways": ["Use find for precise location", "Use tail for fresh log evidence", "Reduce noise early"],
|
||||
"after_class": "Practice explaining the root path and every filter before you run find.",
|
||||
"exercises": [
|
||||
{ "id": "m3_l2_e1", "type": "operation", "title": "Find configuration files under /etc", "hint": "Run find /etc -name '*.conf'.", "success_test": "cmd == \"find /etc -name '*.conf'\" and '.conf' in output", "solution": ["find /etc -name '*.conf'"], "success_msg": "You located configuration files recursively." },
|
||||
{ "id": "m3_l2_e2", "type": "operation", "title": "Find log files under /var/log", "hint": "Run find /var/log -type f.", "success_test": "cmd == 'find /var/log -type f' and '/var/log' in output", "solution": ["find /var/log -type f"], "success_msg": "You filtered the search to files under the log tree." },
|
||||
{ "id": "m3_l2_e3", "type": "operation", "title": "Read the newest auth log line", "hint": "Run tail -n 1 /var/log/auth.log.", "success_test": "cmd == 'tail -n 1 /var/log/auth.log' and 'sandbox_user' in output", "solution": ["tail -n 1 /var/log/auth.log"], "success_msg": "You previewed the most recent auth log line." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m3_l3_preview_and_pipeline",
|
||||
"title": "Preview files with head, tail, and a simple pipeline",
|
||||
"goal": "Read only the part of a file that answers your current question.",
|
||||
"why_it_matters": "Large files are easier to scan when you only open the beginning or end first.",
|
||||
"concepts": ["Top lines", "Bottom lines", "Simple pipelines for narrowing output"],
|
||||
"command": "head / tail / cat | tail",
|
||||
"examples": ["head /etc/passwd", "tail -n 1 /var/log/auth.log", "cat /var/log/auth.log | tail -n 1"],
|
||||
"pitfalls": ["Using cat on a large file when a preview is enough", "Forgetting to change the line count for a focused question"],
|
||||
"scenarios": ["Check whether a file starts with the right structure", "Inspect the last auth event in a log"],
|
||||
"troubleshooting_flow": ["Choose the right end of the file", "Preview a small number of lines", "Switch to grep or full reads only if needed"],
|
||||
"related_commands": ["head", "tail", "cat", "grep"],
|
||||
"classic_view": "Preview commands reduce noise so you can decide whether the file deserves deeper inspection.",
|
||||
"takeaways": ["Use head for structure", "Use tail for recent activity", "Use a pipeline when one command is not enough"],
|
||||
"after_class": "Practice explaining why you chose head or tail before you run the command.",
|
||||
"exercises": [
|
||||
{ "id": "m3_l3_e1", "type": "operation", "title": "Preview the top of passwd", "hint": "Run head /etc/passwd.", "success_test": "cmd == 'head /etc/passwd' and 'root' in output", "solution": ["head /etc/passwd"], "success_msg": "You previewed the beginning of the file." },
|
||||
{ "id": "m3_l3_e2", "type": "operation", "title": "Inspect the newest auth log line", "hint": "Run tail -n 1 /var/log/auth.log.", "success_test": "cmd == 'tail -n 1 /var/log/auth.log' and 'sandbox_user' in output", "solution": ["tail -n 1 /var/log/auth.log"], "success_msg": "You narrowed the file to the most recent line." },
|
||||
{ "id": "m3_l3_e3", "type": "operation", "title": "Use a simple pipeline to preview auth.log", "hint": "Run cat /var/log/auth.log | tail -n 1.", "success_test": "cmd == 'cat /var/log/auth.log | tail -n 1' and 'sandbox_user' in output", "solution": ["cat /var/log/auth.log | tail -n 1"], "success_msg": "You combined two commands to narrow the log quickly." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_4_permission",
|
||||
"title": "🔐 Level 4: 文件权限管理",
|
||||
"description": "chmod、chown、chgrp 权限控制",
|
||||
"challenges": [
|
||||
{"id": "l4_1_ls_l", "title": "查看权限", "description": "使用 ls -l /etc/passwd 查看文件权限", "hint": "ls -l /etc/passwd", "success_test": "'-' in output or 'r' in output", "solution": ["ls -l /etc/passwd"], "success_msg": "👀 权限信息已显示!"},
|
||||
{"id": "l4_2_chmod_755", "title": "修改权限为755", "description": "将 /tmp/testdir 权限改为 rwxr-xr-x (755)", "hint": "chmod 755 /tmp/testdir", "success_test": "cmd == 'chmod 755 /tmp/testdir'", "solution": ["chmod 755 /tmp/testdir"], "success_msg": "🔐 权限修改成功!"},
|
||||
{"id": "l4_3_chmod_644", "title": "修改权限为644", "description": "将 /tmp/testdir/test.txt 权限改为 rw-r--r-- (644)", "hint": "chmod 644 /tmp/testdir/test.txt", "success_test": "cmd == 'chmod 644 /tmp/testdir/test.txt'", "solution": ["chmod 644 /tmp/testdir/test.txt"], "success_msg": "🔐 权限修改成功!"},
|
||||
{"id": "l4_4_chmod_x", "title": "添加执行权限", "description": "给 /tmp/testdir/test.txt 添加可执行权限", "hint": "chmod +x /tmp/testdir/test.txt", "success_test": "cmd == 'chmod +x /tmp/testdir/test.txt'", "solution": ["chmod +x /tmp/testdir/test.txt"], "success_msg": "⚡ 执行权限已添加!"},
|
||||
{"id": "l4_5_chmod_r", "title": "移除读权限", "description": "移除 /tmp/testdir/test.txt 的读权限", "hint": "chmod -r /tmp/testdir/test.txt", "success_test": "cmd == 'chmod -r /tmp/testdir/test.txt'", "solution": ["chmod -r /tmp/testdir/test.txt"], "success_msg": "🚫 读权限已移除!"},
|
||||
{"id": "l4_6_chown", "title": "修改所有者", "description": "将 /tmp/testdir/test.txt 所有者改为 root", "hint": "chown root /tmp/testdir/test.txt", "success_test": "cmd.startswith('chown')", "solution": ["chown root /tmp/testdir/test.txt"], "success_msg": "👤 所有者已修改!"},
|
||||
{"id": "l4_7_chgrp", "title": "修改所属组", "description": "将 /tmp/testdir/test.txt 所属组改为 root", "hint": "chgrp root /tmp/testdir/test.txt", "success_test": "cmd.startswith('chgrp')", "solution": ["chgrp root /tmp/testdir/test.txt"], "success_msg": "👥 所属组已修改!"},
|
||||
{"id": "l4_8_chown_r", "title": "递归修改", "description": "递归修改 /tmp/testdir 及其所有内容的所有者为 root", "hint": "chown -R root /tmp/testdir", "success_test": "cmd.startswith('chown -R')", "solution": ["chown -R root /tmp/testdir"], "success_msg": "🔄 递归修改成功!"}
|
||||
"id": "module_4_operations",
|
||||
"title": "Module 4: Turn commands into operations workflows",
|
||||
"summary": "Use process and network commands as parts of a real troubleshooting chain instead of isolated tools.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m4_l1_process",
|
||||
"title": "Read system pressure with top and ps",
|
||||
"goal": "Spot busy processes and connect process views to system pressure.",
|
||||
"why_it_matters": "Incidents often begin with 'the system feels slow', and process views turn that feeling into evidence.",
|
||||
"concepts": ["Process snapshots", "CPU-heavy processes", "Live system views"],
|
||||
"command": "top / ps",
|
||||
"examples": ["top", "ps aux --sort=-%cpu | head"],
|
||||
"pitfalls": ["Jumping straight to kill before understanding the process role", "Looking at one snapshot without connecting it to symptoms"],
|
||||
"scenarios": ["Find the hottest process when CPU looks high", "Explain which command gives the faster broad view"],
|
||||
"troubleshooting_flow": ["Look at top for the broad picture", "Use ps sorting to confirm the hottest process", "Only then decide the next action"],
|
||||
"related_commands": ["top", "ps", "kill"],
|
||||
"classic_view": "System pressure is easier to reason about when you move from a broad live view to a specific process list.",
|
||||
"takeaways": ["Use top for the live picture", "Use ps for a sortable snapshot", "Diagnose before you terminate anything"],
|
||||
"after_class": "Compare top and ps side by side and explain what each one is better at.",
|
||||
"exercises": [
|
||||
{ "id": "m4_l1_e1", "type": "operation", "title": "Show a live overview", "hint": "Run top.", "success_test": "cmd == 'top' and 'load average' in output", "solution": ["top"], "success_msg": "You captured a live system overview." },
|
||||
{ "id": "m4_l1_e2", "type": "operation", "title": "List processes by CPU usage", "hint": "Run ps aux --sort=-%cpu | head.", "success_test": "cmd == 'ps aux --sort=-%cpu | head' and '%CPU' in output", "solution": ["ps aux --sort=-%cpu | head"], "success_msg": "You ranked processes by CPU usage." },
|
||||
{ "id": "m4_l1_e3", "type": "understanding", "question": "Why should kill come after observation rather than before it?", "answer": "Because you need evidence about what the process is doing and whether it is safe to stop." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m4_l2_network",
|
||||
"title": "Check the network path with ip, ping, ss, and curl",
|
||||
"goal": "Diagnose connectivity step by step from interface state to application response.",
|
||||
"why_it_matters": "Network incidents feel confusing when you mix layers; they become clearer when you inspect one layer at a time.",
|
||||
"concepts": ["Interface address", "Basic connectivity", "Listening ports", "Application-level validation"],
|
||||
"command": "ip / ping / ss / curl",
|
||||
"examples": ["ip addr", "ping 127.0.0.1", "ss -ltnp", "curl http://127.0.0.1"],
|
||||
"pitfalls": ["Treating DNS, port, and app failures as one category", "Checking a port without making a real request"],
|
||||
"scenarios": ["A local service looks up but the app still feels unavailable", "You need to confirm whether the issue is address, reachability, port, or HTTP behavior"],
|
||||
"troubleshooting_flow": ["Check interface state with ip addr", "Test connectivity with ping", "Check sockets with ss", "Validate the app layer with curl"],
|
||||
"related_commands": ["ip", "ping", "ss", "curl", "dig"],
|
||||
"classic_view": "The cleanest network diagnosis moves from low-level reachability to the final application response.",
|
||||
"takeaways": ["Do not skip layers", "Confirm a port before blaming the app", "Use curl to verify user-facing behavior"],
|
||||
"after_class": "Repeat the sequence ip -> ping -> ss -> curl until the layer transitions feel natural.",
|
||||
"exercises": [
|
||||
{ "id": "m4_l2_e1", "type": "operation", "title": "Inspect interface addresses", "hint": "Run ip addr.", "success_test": "cmd == 'ip addr' and 'inet' in output", "solution": ["ip addr"], "success_msg": "You confirmed interface and address information." },
|
||||
{ "id": "m4_l2_e2", "type": "operation", "title": "Test local connectivity", "hint": "Run ping 127.0.0.1.", "success_test": "cmd == 'ping 127.0.0.1' and 'packet loss' in output", "solution": ["ping 127.0.0.1"], "success_msg": "You verified the local connectivity layer." },
|
||||
{ "id": "m4_l2_e3", "type": "operation", "title": "Validate the application response", "hint": "Run curl http://127.0.0.1.", "success_test": "cmd == 'curl http://127.0.0.1' and 'hello localhost' in output", "solution": ["curl http://127.0.0.1"], "success_msg": "You completed the network path all the way to the application layer." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m4_l3_service",
|
||||
"title": "Trace service health with systemctl and journalctl",
|
||||
"goal": "Move from service state to recent logs in a deliberate sequence.",
|
||||
"why_it_matters": "Service incidents are easier to solve when you separate service state from application logs.",
|
||||
"concepts": ["Service unit state", "Recent journal lines", "From status to explanation"],
|
||||
"command": "systemctl / journalctl",
|
||||
"examples": ["systemctl status nginx", "journalctl -n 20", "systemctl restart nginx"],
|
||||
"pitfalls": ["Restarting a service without checking why it failed", "Reading status without following the logs"],
|
||||
"scenarios": ["A service is reported down or unstable", "You need the last error before considering a restart"],
|
||||
"troubleshooting_flow": ["Read service state with systemctl status", "Open the recent journal lines", "Use the message to guide the next repair"],
|
||||
"related_commands": ["systemctl", "journalctl", "ss"],
|
||||
"classic_view": "Service status tells you what is happening; logs tell you why.",
|
||||
"takeaways": ["Start with status", "Use logs for explanation", "Restart only after you understand the failure"],
|
||||
"after_class": "Say out loud what systemctl tells you that journalctl does not, and vice versa.",
|
||||
"exercises": [
|
||||
{ "id": "m4_l3_e1", "type": "operation", "title": "Check nginx service status", "hint": "Run systemctl status nginx.", "success_test": "cmd == 'systemctl status nginx' and 'Active:' in output", "solution": ["systemctl status nginx"], "success_msg": "You inspected the service state." },
|
||||
{ "id": "m4_l3_e2", "type": "operation", "title": "Read recent journal lines", "hint": "Run journalctl -n 20.", "success_test": "cmd == 'journalctl -n 20' and 'Started nginx.service' in output", "solution": ["journalctl -n 20"], "success_msg": "You pulled recent journal evidence." },
|
||||
{ "id": "m4_l3_e3", "type": "operation", "title": "Simulate a service restart command", "hint": "Run systemctl restart nginx.", "success_test": "cmd == 'systemctl restart nginx' and 'completed for nginx' in output", "solution": ["systemctl restart nginx"], "success_msg": "You practiced the restart command after inspection." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_5_search",
|
||||
"title": "🔍 Level 5: 搜索与过滤",
|
||||
"description": "find、grep、which、whereis 搜索命令",
|
||||
"challenges": [
|
||||
{"id": "l5_1_find_name", "title": "按名称查找", "description": "在 /etc 下查找所有 .conf 文件", "hint": "find /etc -name '*.conf'", "success_test": "'.conf' in output", "solution": ["find /etc -name '*.conf'"], "success_msg": "🔍 文件已找到!"},
|
||||
{"id": "l5_2_find_type", "title": "按类型查找", "description": "查找 /tmp 下的所有目录", "hint": "find /tmp -type d", "success_test": "cmd.startswith('find') and '-type d' in cmd", "solution": ["find /tmp -type d"], "success_msg": "📁 目录已找到!"},
|
||||
{"id": "l5_3_find_size", "title": "按大小查找", "description": "查找 /var/log 下大于 1M 的文件", "hint": "find /var/log -size +1M", "success_test": "cmd.startswith('find') and '-size' in cmd", "solution": ["find /var/log -size +1M"], "success_msg": "📏 大文件已找到!"},
|
||||
{"id": "l5_4_find_exec", "title": "执行操作", "description": "查找 /tmp 下所有 .log 文件并删除", "hint": "find /tmp -name '*.log' -exec rm {} \\;", "success_test": "'-exec' in cmd", "solution": ["find /tmp -name '*.log' -exec rm {} \\;"], "success_msg": "🗑️ 操作执行成功!"},
|
||||
{"id": "l5_5_grep", "title": "文本搜索", "description": "在 /etc/passwd 中搜索 root 用户", "hint": "grep root /etc/passwd", "success_test": "'root' in output", "solution": ["grep root /etc/passwd"], "success_msg": "🎯 搜索成功!"},
|
||||
{"id": "l5_6_grep_i", "title": "忽略大小写", "description": "使用 grep -i 搜索 ROOT(不区分大小写)", "hint": "grep -i root /etc/passwd", "success_test": "'-i' in cmd", "solution": ["grep -i root /etc/passwd"], "success_msg": "🔤 忽略大小写搜索成功!"},
|
||||
{"id": "l5_7_grep_v", "title": "反向匹配", "description": "显示 /etc/passwd 中不包含 nologin 的行", "hint": "grep -v nologin /etc/passwd", "success_test": "'-v' in cmd", "solution": ["grep -v nologin /etc/passwd"], "success_msg": "🔄 反向匹配成功!"},
|
||||
{"id": "l5_8_grep_n", "title": "显示行号", "description": "使用 grep -n 显示匹配行的行号", "hint": "grep -n root /etc/passwd", "success_test": "'-n' in cmd and ':' in output", "solution": ["grep -n root /etc/passwd"], "success_msg": "🔢 行号已显示!"},
|
||||
{"id": "l5_9_which", "title": "查找命令位置", "description": "查找 ls 命令的位置", "hint": "which ls", "success_test": "'/bin' in output or '/usr' in output", "solution": ["which ls"], "success_msg": "📍 命令位置已找到!"},
|
||||
{"id": "l5_10_whereis", "title": "查找相关文件", "description": "使用 whereis 查找 ls 命令的相关文件", "hint": "whereis ls", "success_test": "cmd.startswith('whereis')", "solution": ["whereis ls"], "success_msg": "📚 相关文件已找到!"}
|
||||
"id": "module_5_incidents",
|
||||
"title": "Module 5: Practice incident playbooks",
|
||||
"summary": "Use the commands you learned in layered incident drills so the tools feel connected to real operations work.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m5_l1_disk",
|
||||
"title": "Incident drill: investigate disk pressure",
|
||||
"goal": "Move from broad disk visibility to specific directory evidence.",
|
||||
"why_it_matters": "Disk pressure can break writes, logs, and services, so you need a repeatable inspection path.",
|
||||
"concepts": ["Filesystem usage", "Directory usage", "Large-file hunting"],
|
||||
"command": "df / du / find",
|
||||
"examples": ["df -h", "du -sh /var/log", "find /var/log -type f"],
|
||||
"pitfalls": ["Stopping at df without drilling into the heavy directory", "Deleting files before confirming what they are for"],
|
||||
"scenarios": ["A host reports that disk usage is full", "A log directory appears to be growing too quickly"],
|
||||
"troubleshooting_flow": ["Confirm which filesystem is full", "Measure the likely heavy directory", "Locate concrete files before deciding cleanup"],
|
||||
"related_commands": ["df", "du", "find", "sort"],
|
||||
"classic_view": "Disk incidents are easier when you move from filesystem view to directory view to file view in order.",
|
||||
"takeaways": ["Use df for the broad picture", "Use du for directory evidence", "Use find before deleting anything"],
|
||||
"after_class": "Practice saying which layer each command belongs to: filesystem, directory, or file.",
|
||||
"exercises": [
|
||||
{ "id": "m5_l1_e1", "type": "operation", "title": "Check filesystem usage", "hint": "Run df -h.", "success_test": "cmd == 'df -h' and 'Filesystem' in output", "solution": ["df -h"], "success_msg": "You captured the broad disk usage picture." },
|
||||
{ "id": "m5_l1_e2", "type": "operation", "title": "Measure /var/log usage", "hint": "Run du -sh /var/log.", "success_test": "cmd == 'du -sh /var/log' and '/var/log' in output", "solution": ["du -sh /var/log"], "success_msg": "You narrowed the issue to a directory-level view." },
|
||||
{ "id": "m5_l1_e3", "type": "operation", "title": "Locate log files under /var/log", "hint": "Run find /var/log -type f.", "success_test": "cmd == 'find /var/log -type f' and '/var/log' in output", "solution": ["find /var/log -type f"], "success_msg": "You drilled down to the file layer." },
|
||||
{ "id": "m5_l1_e4", "type": "scenario", "question": "Why is deleting files before directory and file inspection risky?", "answer": "Because you can remove useful evidence or the wrong data before you understand what is consuming space." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m5_l2_auth",
|
||||
"title": "Incident drill: investigate login or permission failures",
|
||||
"goal": "Link identity, account records, and recent auth logs into one diagnosis sequence.",
|
||||
"why_it_matters": "Access problems often mix user identity, account state, and authentication evidence.",
|
||||
"concepts": ["Current identity", "Account presence", "Recent authentication history"],
|
||||
"command": "whoami / id / grep / tail",
|
||||
"examples": ["whoami", "id", "grep sandbox_user /etc/passwd", "cat /var/log/auth.log | tail -n 1"],
|
||||
"pitfalls": ["Guessing a password issue without checking logs", "Ignoring account records and group membership"],
|
||||
"scenarios": ["A login fails repeatedly", "A script exists but still cannot be used by the current user"],
|
||||
"troubleshooting_flow": ["Confirm the current identity", "Verify the target account exists", "Read the newest auth evidence"],
|
||||
"related_commands": ["whoami", "id", "grep", "tail"],
|
||||
"classic_view": "Access issues are easier when you inspect identity, account records, and logs as one chain.",
|
||||
"takeaways": ["Confirm the user context first", "Use account files as evidence", "Trust logs more than guesses"],
|
||||
"after_class": "Repeat the identity -> account -> log sequence until it feels like one habit.",
|
||||
"exercises": [
|
||||
{ "id": "m5_l2_e1", "type": "operation", "title": "Confirm your current identity", "hint": "Run whoami.", "success_test": "cmd == 'whoami' and 'sandbox_user' in output", "solution": ["whoami"], "success_msg": "You confirmed the current shell identity." },
|
||||
{ "id": "m5_l2_e2", "type": "operation", "title": "Inspect uid and group information", "hint": "Run id.", "success_test": "cmd == 'id' and 'uid=' in output", "solution": ["id"], "success_msg": "You inspected the uid and group context." },
|
||||
{ "id": "m5_l2_e3", "type": "operation", "title": "Check whether sandbox_user exists in passwd", "hint": "Run grep sandbox_user /etc/passwd.", "success_test": "cmd == 'grep sandbox_user /etc/passwd' and 'sandbox_user' in output", "solution": ["grep sandbox_user /etc/passwd"], "success_msg": "You verified the account record." },
|
||||
{ "id": "m5_l2_e4", "type": "operation", "title": "Read the newest auth log line", "hint": "Run cat /var/log/auth.log | tail -n 1.", "success_test": "cmd == 'cat /var/log/auth.log | tail -n 1' and 'sandbox_user' in output", "solution": ["cat /var/log/auth.log | tail -n 1"], "success_msg": "You retrieved recent authentication evidence." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m5_l3_service_path",
|
||||
"title": "Incident drill: service is up, but the application still feels broken",
|
||||
"goal": "Check service state, listening sockets, and application output in one guided drill.",
|
||||
"why_it_matters": "A service can look healthy at one layer but still fail at another layer.",
|
||||
"concepts": ["Service state", "Socket listening state", "Application response validation"],
|
||||
"command": "systemctl / ss / curl / journalctl",
|
||||
"examples": ["systemctl status nginx", "ss -ltnp", "curl http://127.0.0.1", "journalctl -n 20"],
|
||||
"pitfalls": ["Trusting systemctl alone without checking sockets or a real request", "Stopping at ss without making an application-layer call"],
|
||||
"scenarios": ["A team says the service is up but the page still feels unavailable", "You need to prove whether the problem is service state, port state, or HTTP behavior"],
|
||||
"troubleshooting_flow": ["Check systemctl for service state", "Check ss for listening ports", "Use curl to verify the real response", "Open journal logs if behavior still looks wrong"],
|
||||
"related_commands": ["systemctl", "ss", "curl", "journalctl"],
|
||||
"classic_view": "Healthy service state does not guarantee healthy application behavior, so finish the chain with a real request.",
|
||||
"takeaways": ["Check multiple layers", "Do not confuse a listening port with a healthy app", "End the chain with a real request and logs when needed"],
|
||||
"after_class": "Practice explaining which layer each command validates: service, socket, application, or logs.",
|
||||
"exercises": [
|
||||
{ "id": "m5_l3_e1", "type": "operation", "title": "Check nginx service state", "hint": "Run systemctl status nginx.", "success_test": "cmd == 'systemctl status nginx' and 'Active:' in output", "solution": ["systemctl status nginx"], "success_msg": "You confirmed the service layer state." },
|
||||
{ "id": "m5_l3_e2", "type": "operation", "title": "Check listening sockets", "hint": "Run ss -ltnp.", "success_test": "cmd == 'ss -ltnp' and 'LISTEN' in output", "solution": ["ss -ltnp"], "success_msg": "You confirmed the socket layer." },
|
||||
{ "id": "m5_l3_e3", "type": "operation", "title": "Make a real application request", "hint": "Run curl http://127.0.0.1.", "success_test": "cmd == 'curl http://127.0.0.1' and 'hello localhost' in output", "solution": ["curl http://127.0.0.1"], "success_msg": "You verified the final application response layer." },
|
||||
{ "id": "m5_l3_e4", "type": "operation", "title": "Open recent journal lines", "hint": "Run journalctl -n 20.", "success_test": "cmd == 'journalctl -n 20' and 'Started nginx.service' in output", "solution": ["journalctl -n 20"], "success_msg": "You added log evidence to the service drill." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_6_user",
|
||||
"title": "👥 Level 6: 用户和组管理",
|
||||
"description": "useradd、usermod、passwd、group 管理",
|
||||
"challenges": [
|
||||
{"id": "l6_1_whoami", "title": "查看当前用户", "description": "使用 whoami 查看当前登录用户名", "hint": "whoami", "success_test": "len(output.strip()) > 0", "solution": ["whoami"], "success_msg": "👤 当前用户已显示!"},
|
||||
{"id": "l6_2_id", "title": "查看用户ID", "description": "使用 id 查看当前用户的 UID、GID 和所属组", "hint": "id", "success_test": "'uid' in output.lower()", "solution": ["id"], "success_msg": "🆔 用户信息已显示!"},
|
||||
{"id": "l6_3_w", "title": "查看登录用户", "description": "使用 w 查看当前登录系统的用户", "hint": "w", "success_test": "'USER' in output or 'TTY' in output", "solution": ["w"], "success_msg": "👥 登录用户已显示!"},
|
||||
{"id": "l6_4_last", "title": "查看登录历史", "description": "使用 last 查看最近的登录记录", "hint": "last", "success_test": "len(output) > 10", "solution": ["last"], "success_msg": "📜 登录历史已显示!"},
|
||||
{"id": "l6_5_cat_passwd", "title": "查看用户列表", "description": "查看 /etc/passwd 文件了解系统用户", "hint": "cat /etc/passwd", "success_test": "':' in output", "solution": ["cat /etc/passwd"], "success_msg": "📋 用户列表已显示!"},
|
||||
{"id": "l6_6_cat_group", "title": "查看组列表", "description": "查看 /etc/group 文件了解系统用户组", "hint": "cat /etc/group", "success_test": "':' in output", "solution": ["cat /etc/group"], "success_msg": "📋 组列表已显示!"},
|
||||
{"id": "l6_7_passwd", "title": "修改密码", "description": "使用 passwd 修改当前用户密码(输入当前密码和新密码)", "hint": "passwd", "success_test": "cmd == 'passwd'", "solution": ["passwd"], "success_msg": "🔑 密码修改命令已执行!"},
|
||||
{"id": "l6_8_su", "title": "切换用户", "description": "使用 su - 切换到 root 用户", "hint": "su -", "success_test": "cmd.startswith('su')", "solution": ["su -", "su"], "success_msg": "🔄 用户切换命令已执行!"}
|
||||
"id": "module_6_shell_environment",
|
||||
"title": "Module 6: Work faster in the shell",
|
||||
"summary": "Use environment variables, aliases, history, and simple text pipelines to make Linux work feel repeatable instead of manual.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m6_l1_env_alias",
|
||||
"title": "Control shell context with env, export, and alias",
|
||||
"goal": "Understand how shell state changes affect later commands and sessions.",
|
||||
"why_it_matters": "A lot of Linux productivity comes from shaping the shell so common work is easier to repeat safely.",
|
||||
"concepts": ["Environment variables", "Exported shell state", "Command aliases"],
|
||||
"command": "env / export / alias",
|
||||
"examples": ["env", "export APP_MODE=lab", "alias ll='ls -l'"],
|
||||
"pitfalls": ["Assuming an echoed value is the same as an exported variable", "Creating aliases without understanding the original command"],
|
||||
"scenarios": ["Prepare a temporary runtime flag", "Shorten a repeated command pattern"],
|
||||
"troubleshooting_flow": ["Inspect the current shell state", "Change one variable or alias", "Verify the shell reflects the change"],
|
||||
"related_commands": ["env", "export", "alias", "echo"],
|
||||
"classic_view": "The shell is not just a place to type commands; it is a working environment you can shape.",
|
||||
"takeaways": ["Use env to inspect state", "Use export to persist a shell variable for child processes", "Use aliases carefully and intentionally"],
|
||||
"after_class": "Practice changing one variable and one alias, then explain what effect each change has.",
|
||||
"exercises": [
|
||||
{ "id": "m6_l1_e1", "type": "operation", "title": "Inspect the current environment", "hint": "Run env.", "success_test": "cmd == 'env' and 'HOME=' in output", "solution": ["env"], "success_msg": "You inspected the current shell environment." },
|
||||
{ "id": "m6_l1_e2", "type": "operation", "title": "Export a temporary variable", "hint": "Run export APP_MODE=lab.", "success_test": "cmd == 'export APP_MODE=lab' and 'APP_MODE=lab' in output", "solution": ["export APP_MODE=lab"], "success_msg": "You exported a shell variable successfully." },
|
||||
{ "id": "m6_l1_e3", "type": "operation", "title": "Create a listing alias", "hint": "Run alias ll='ls -l'.", "success_test": "cmd == \"alias ll='ls -l'\" and \"alias ll='ls -l'\" in output", "solution": ["alias ll='ls -l'"], "success_msg": "You created a shell alias." },
|
||||
{ "id": "m6_l1_e4", "type": "scenario", "question": "Why is export different from simply printing a value with echo?", "answer": "Because export changes the shell environment for later commands, while echo only prints text once." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m6_l2_history_replay",
|
||||
"title": "Use history, date, and clear to manage command sessions",
|
||||
"goal": "Review what you ran, time-stamp your work, and keep the terminal readable during longer sessions.",
|
||||
"why_it_matters": "Operations work often depends on replaying what happened and keeping a clean command narrative.",
|
||||
"concepts": ["Command history", "Session readability", "Time awareness"],
|
||||
"command": "history / date / clear",
|
||||
"examples": ["history", "date", "clear"],
|
||||
"pitfalls": ["Running many commands without keeping track of sequence", "Treating a terminal as disposable when the command history is useful evidence"],
|
||||
"scenarios": ["Reconstruct what you tried during an incident", "Record the time before a change or restart"],
|
||||
"troubleshooting_flow": ["Review your recent commands", "Record or inspect the current time", "Clear the screen only after capturing what matters"],
|
||||
"related_commands": ["history", "date", "clear", "echo"],
|
||||
"classic_view": "A strong operator can explain not just what they ran, but in what order and when.",
|
||||
"takeaways": ["Use history as lightweight evidence", "Use date to anchor actions in time", "Clear the screen only when it helps readability"],
|
||||
"after_class": "Practice summarizing a small command session from history without looking at your notes.",
|
||||
"exercises": [
|
||||
{ "id": "m6_l2_e1", "type": "operation", "title": "Show your recent command history", "hint": "Run history.", "success_test": "cmd == 'history' and len(output) >= 0", "solution": ["history"], "success_msg": "You reviewed the shell history." },
|
||||
{ "id": "m6_l2_e2", "type": "operation", "title": "Print the current date and time", "hint": "Run date.", "success_test": "cmd == 'date' and 'CST' in output", "solution": ["date"], "success_msg": "You time-stamped the session." },
|
||||
{ "id": "m6_l2_e3", "type": "operation", "title": "Clear the terminal view", "hint": "Run clear.", "success_test": "cmd == 'clear' and 'screen cleared' in output", "solution": ["clear"], "success_msg": "You reset the visible terminal output." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m6_l3_text_pipeline",
|
||||
"title": "Reduce noise with sort, uniq, cut, awk, and sed",
|
||||
"goal": "Use small text-processing tools to transform output instead of reading everything manually.",
|
||||
"why_it_matters": "Linux operations is easier when you can shape output into something small enough to reason about.",
|
||||
"concepts": ["Pipeline thinking", "Sorting and deduplication", "Field extraction", "Quick text transforms"],
|
||||
"command": "sort / uniq / cut / awk / sed",
|
||||
"examples": ["cat /var/log/auth.log | sort", "cat /var/log/auth.log | uniq", "cat /etc/passwd | cut -d: -f1"],
|
||||
"pitfalls": ["Running multiple commands manually when a pipeline would be clearer", "Transforming output without understanding what field you are extracting"],
|
||||
"scenarios": ["Normalize repeated lines before inspection", "Extract usernames from passwd"],
|
||||
"troubleshooting_flow": ["Start with raw output", "Apply one transform at a time", "Stop when the output is small enough to answer the question"],
|
||||
"related_commands": ["sort", "uniq", "cut", "awk", "sed", "grep"],
|
||||
"classic_view": "Pipelines are how Linux turns a large question into a sequence of smaller, readable steps.",
|
||||
"takeaways": ["Use sort and uniq to reduce repetition", "Use cut for simple field extraction", "Treat awk and sed as focused shaping tools rather than magic"],
|
||||
"after_class": "Practice explaining what each pipeline stage contributes before you run the full chain.",
|
||||
"exercises": [
|
||||
{ "id": "m6_l3_e1", "type": "operation", "title": "Sort auth log lines", "hint": "Run cat /var/log/auth.log | sort.", "success_test": "cmd == 'cat /var/log/auth.log | sort' and len(output) > 0", "solution": ["cat /var/log/auth.log | sort"], "success_msg": "You sorted the auth log lines." },
|
||||
{ "id": "m6_l3_e2", "type": "operation", "title": "Deduplicate auth log lines", "hint": "Run cat /var/log/auth.log | uniq.", "success_test": "cmd == 'cat /var/log/auth.log | uniq' and len(output) > 0", "solution": ["cat /var/log/auth.log | uniq"], "success_msg": "You reduced duplicate lines with uniq." },
|
||||
{ "id": "m6_l3_e3", "type": "operation", "title": "Extract the first passwd field", "hint": "Run cat /etc/passwd | cut -d: -f1.", "success_test": "cmd == 'cat /etc/passwd | cut -d: -f1' and len(output) > 0", "solution": ["cat /etc/passwd | cut -d: -f1"], "success_msg": "You extracted a single field from passwd." },
|
||||
{ "id": "m6_l3_e4", "type": "scenario", "question": "Why do pipelines help more than manually copying output into another tool?", "answer": "Because they preserve a clear, repeatable transformation chain directly in the shell." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_7_disk",
|
||||
"title": "💾 Level 7: 磁盘管理",
|
||||
"description": "df、du、fdisk、mount 磁盘操作",
|
||||
"challenges": [
|
||||
{"id": "l7_1_df", "title": "查看磁盘空间", "description": "使用 df 查看文件系统磁盘空间使用情况", "hint": "df", "success_test": "'Filesystem' in output or '文件系统' in output", "solution": ["df"], "success_msg": "💾 磁盘空间已显示!"},
|
||||
{"id": "l7_2_df_h", "title": "人类可读格式", "description": "使用 df -h 以人类可读格式显示磁盘空间", "hint": "df -h", "success_test": "'G' in output or 'M' in output or 'K' in output", "solution": ["df -h"], "success_msg": "📊 磁盘空间(易读格式)!"},
|
||||
{"id": "l7_3_du", "title": "查看目录大小", "description": "使用 du 查看 /tmp 目录的大小", "hint": "du /tmp", "success_test": "len(output) > 0", "solution": ["du /tmp"], "success_msg": "📏 目录大小已显示!"},
|
||||
{"id": "l7_4_du_sh", "title": "查看总大小", "description": "使用 du -sh 查看 /tmp 的总大小", "hint": "du -sh /tmp", "success_test": "'-sh' in cmd", "solution": ["du -sh /tmp"], "success_msg": "📊 总大小已显示!"},
|
||||
{"id": "l7_5_du_sort", "title": "排序查看", "description": "查看 /tmp 下最大的 5 个目录", "hint": "du -sh /tmp/* | sort -rh | head -5", "success_test": "'sort' in cmd", "solution": ["du -sh /tmp/* | sort -rh | head -5"], "success_msg": "🏆 最大目录已排序!"},
|
||||
{"id": "l7_6_mount", "title": "查看挂载点", "description": "使用 mount 查看已挂载的文件系统", "hint": "mount", "success_test": "'on' in output or 'type' in output", "solution": ["mount"], "success_msg": "🔗 挂载点已显示!"},
|
||||
{"id": "l7_7_fdisk", "title": "查看分区表", "description": "使用 fdisk -l 查看磁盘分区表(需要 root 权限)", "hint": "fdisk -l", "success_test": "cmd.startswith('fdisk')", "solution": ["fdisk -l"], "success_msg": "💿 分区表已显示!"}
|
||||
"id": "module_7_packages_and_delivery",
|
||||
"title": "Module 7: Locate tools, inspect packages, and move artifacts",
|
||||
"summary": "Learn where commands live, how package managers differ, and how archives or background jobs support basic delivery work.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m7_l1_locate_tools",
|
||||
"title": "Locate binaries with which and whereis",
|
||||
"goal": "Understand where commands come from before changing PATHs or package assumptions.",
|
||||
"why_it_matters": "Operators often need to prove which binary is running and where supporting docs or manpages live.",
|
||||
"concepts": ["Binary location", "PATH lookup", "Tool discovery"],
|
||||
"command": "which / whereis",
|
||||
"examples": ["which curl", "which ls", "whereis grep"],
|
||||
"pitfalls": ["Assuming the same command path on every server", "Changing PATH without first proving what is currently used"],
|
||||
"scenarios": ["Check where curl is installed", "Find the shell-visible ls binary"],
|
||||
"troubleshooting_flow": ["Ask which binary the shell resolves", "Use whereis for broader discovery", "Only then adjust your assumptions or PATH"],
|
||||
"related_commands": ["which", "whereis", "env"],
|
||||
"classic_view": "Before you replace a binary or adjust PATH, prove what the shell is already resolving.",
|
||||
"takeaways": ["Use which for shell resolution", "Use whereis for broader lookup", "Do not guess command paths"],
|
||||
"after_class": "Compare which and whereis on two commands and explain why their outputs differ.",
|
||||
"exercises": [
|
||||
{ "id": "m7_l1_e1", "type": "operation", "title": "Locate curl", "hint": "Run which curl.", "success_test": "cmd == 'which curl' and '/usr/bin/curl' in output", "solution": ["which curl"], "success_msg": "You located the curl binary." },
|
||||
{ "id": "m7_l1_e2", "type": "operation", "title": "Locate ls", "hint": "Run which ls.", "success_test": "cmd == 'which ls' and '/bin/ls' in output", "solution": ["which ls"], "success_msg": "You located the ls binary." },
|
||||
{ "id": "m7_l1_e3", "type": "operation", "title": "Broaden the search for grep", "hint": "Run whereis grep.", "success_test": "cmd == 'whereis grep' and 'grep:' in output", "solution": ["whereis grep"], "success_msg": "You used whereis for broader command discovery." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m7_l2_package_basics",
|
||||
"title": "Read package tool output with apt, yum, rpm, and dpkg",
|
||||
"goal": "Recognize the common package tools you will encounter across distributions.",
|
||||
"why_it_matters": "Even if you automate package installs later, you still need to understand the native package vocabulary of the host.",
|
||||
"concepts": ["Debian vs RPM ecosystems", "Package listing output", "Distribution-specific tooling"],
|
||||
"command": "apt / yum / rpm / dpkg",
|
||||
"examples": ["apt", "yum", "rpm", "dpkg"],
|
||||
"pitfalls": ["Assuming apt exists on every Linux server", "Reading package output without identifying the distro context first"],
|
||||
"scenarios": ["Check which package tool family a server likely uses", "Read enough package output to orient yourself before change work"],
|
||||
"troubleshooting_flow": ["Identify the package family", "Read listing output", "Only then plan installs or version checks"],
|
||||
"related_commands": ["apt", "yum", "rpm", "dpkg", "which"],
|
||||
"classic_view": "Package commands are not interchangeable, so distribution context matters before action.",
|
||||
"takeaways": ["Recognize Debian-style and RPM-style tooling", "Read package output without guessing", "Use package family as part of host context"],
|
||||
"after_class": "Practice explaining which systems are more likely to use apt vs yum or rpm.",
|
||||
"exercises": [
|
||||
{ "id": "m7_l2_e1", "type": "operation", "title": "Inspect apt output", "hint": "Run apt.", "success_test": "cmd == 'apt' and 'Reading package lists' in output", "solution": ["apt"], "success_msg": "You inspected Debian-style package manager output." },
|
||||
{ "id": "m7_l2_e2", "type": "operation", "title": "Inspect yum output", "hint": "Run yum.", "success_test": "cmd == 'yum' and 'Installed Packages' in output", "solution": ["yum"], "success_msg": "You inspected RPM-style package manager output." },
|
||||
{ "id": "m7_l2_e3", "type": "operation", "title": "Inspect rpm output", "hint": "Run rpm.", "success_test": "cmd == 'rpm' and 'bash-' in output", "solution": ["rpm"], "success_msg": "You inspected a package-level rpm response." },
|
||||
{ "id": "m7_l2_e4", "type": "operation", "title": "Inspect dpkg output", "hint": "Run dpkg.", "success_test": "cmd == 'dpkg' and 'ii bash' in output", "solution": ["dpkg"], "success_msg": "You inspected a Debian package-level response." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m7_l3_archives_and_background",
|
||||
"title": "Handle archives and background jobs with tar and nohup",
|
||||
"goal": "Prepare simple deployment artifacts and understand why background execution changes how you observe a job.",
|
||||
"why_it_matters": "Basic delivery work often includes packaging files, extracting them, and running something outside the current terminal.",
|
||||
"concepts": ["Archive creation", "Archive extraction", "Background job output"],
|
||||
"command": "tar / nohup",
|
||||
"examples": ["tar -czf /tmp/lab.tar.gz /tmp/lab", "tar -xzf /tmp/lab.tar.gz -C /tmp/lab_restore", "nohup python3 /projects/backend/app.py"],
|
||||
"pitfalls": ["Treating an archive like a backup without verifying extraction", "Starting a job with nohup without thinking about its output destination"],
|
||||
"scenarios": ["Bundle a small workspace", "Extract an artifact into a staging path", "Run a long task after disconnecting"],
|
||||
"troubleshooting_flow": ["Create the artifact", "Extract it into a clean target", "Use nohup only when the process should survive the terminal"],
|
||||
"related_commands": ["tar", "nohup", "ps", "ls"],
|
||||
"classic_view": "Packaging and background execution are useful, but only when you stay aware of where files and output go.",
|
||||
"takeaways": ["Use tar for simple packaging", "Verify extraction paths", "Use nohup deliberately, not by habit"],
|
||||
"after_class": "Practice explaining what changed in the filesystem after create and extract operations.",
|
||||
"exercises": [
|
||||
{ "id": "m7_l3_e1", "type": "operation", "title": "Create a tar archive", "hint": "Run tar -czf /tmp/lab.tar.gz /tmp/lab.", "success_test": "cmd == 'tar -czf /tmp/lab.tar.gz /tmp/lab' and exists('/tmp/lab.tar.gz')", "solution": ["tar -czf /tmp/lab.tar.gz /tmp/lab"], "success_msg": "You created an archive artifact." },
|
||||
{ "id": "m7_l3_e2", "type": "operation", "title": "Extract the tar archive", "hint": "Run tar -xzf /tmp/lab.tar.gz -C /tmp/lab_restore.", "success_test": "cmd == 'tar -xzf /tmp/lab.tar.gz -C /tmp/lab_restore' and exists('/tmp/lab_restore')", "solution": ["tar -xzf /tmp/lab.tar.gz -C /tmp/lab_restore"], "success_msg": "You extracted the archive into a clean target directory." },
|
||||
{ "id": "m7_l3_e3", "type": "operation", "title": "Start a background-friendly command", "hint": "Run nohup python3 /projects/backend/app.py.", "success_test": "cmd == 'nohup python3 /projects/backend/app.py' and 'nohup.out' in output", "solution": ["nohup python3 /projects/backend/app.py"], "success_msg": "You practiced a nohup-style launch." },
|
||||
{ "id": "m7_l3_e4", "type": "scenario", "question": "Why should you still verify extraction or output paths after using tar or nohup?", "answer": "Because packaging or backgrounding can succeed syntactically while still writing to the wrong place." }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_8_process",
|
||||
"title": "⚙️ Level 8: 进程管理",
|
||||
"description": "ps、top、kill、nohup 进程控制",
|
||||
"challenges": [
|
||||
{"id": "l8_1_ps", "title": "查看进程", "description": "使用 ps 查看当前用户的进程", "hint": "ps", "success_test": "'PID' in output", "solution": ["ps"], "success_msg": "📋 进程列表已显示!"},
|
||||
{"id": "l8_2_ps_aux", "title": "查看所有进程", "description": "使用 ps aux 查看系统所有进程", "hint": "ps aux", "success_test": "'%CPU' in output or 'RSS' in output", "solution": ["ps aux"], "success_msg": "📊 所有进程已显示!"},
|
||||
{"id": "l8_3_ps_grep", "title": "查找特定进程", "description": "查找包含 ssh 的进程", "hint": "ps aux | grep ssh", "success_test": "'|' in cmd", "solution": ["ps aux | grep ssh"], "success_msg": "🎯 进程已找到!"},
|
||||
{"id": "l8_4_top", "title": "实时进程监控", "description": "使用 top 实时查看进程(按 q 退出)", "hint": "top", "success_test": "cmd == 'top'", "solution": ["top"], "success_msg": "📈 实时监控已启动!"},
|
||||
{"id": "l8_5_kill", "title": "终止进程", "description": "使用 kill 命令(查看 PID 后终止)", "hint": "kill PID", "success_test": "cmd.startswith('kill')", "solution": ["kill 1234"], "success_msg": "💀 终止命令已执行!"},
|
||||
{"id": "l8_6_kill9", "title": "强制终止", "description": "使用 kill -9 强制终止进程", "hint": "kill -9 PID", "success_test": "'-9' in cmd", "solution": ["kill -9 1234"], "success_msg": "💥 强制终止已执行!"},
|
||||
{"id": "l8_7_pkill", "title": "按名称终止", "description": "使用 pkill 按进程名终止", "hint": "pkill process_name", "success_test": "cmd.startswith('pkill')", "solution": ["pkill python"], "success_msg": "🎯 按名称终止已执行!"},
|
||||
{"id": "l8_8_nohup", "title": "后台运行", "description": "使用 nohup 让命令在后台运行", "hint": "nohup command &", "success_test": "cmd.startswith('nohup')", "solution": ["nohup sleep 10 &"], "success_msg": "🌙 后台运行已设置!"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_9_network",
|
||||
"title": "🌐 Level 9: 网络管理",
|
||||
"description": "ifconfig、ping、netstat、curl 网络命令",
|
||||
"challenges": [
|
||||
{"id": "l9_1_ifconfig", "title": "查看网卡", "description": "使用 ifconfig 查看网络接口配置", "hint": "ifconfig", "success_test": "'inet' in output or 'eth' in output or 'lo' in output", "solution": ["ifconfig"], "success_msg": "🎴 网卡信息已显示!"},
|
||||
{"id": "l9_2_ip_addr", "title": "现代方式查看", "description": "使用 ip addr 查看网络接口", "hint": "ip addr", "success_test": "cmd.startswith('ip addr')", "solution": ["ip addr"], "success_msg": "🎴 网络接口已显示!"},
|
||||
{"id": "l9_3_ping", "title": "测试连通性", "description": "ping 127.0.0.1 测试本地网络", "hint": "ping -c 4 127.0.0.1", "success_test": "cmd.startswith('ping')", "solution": ["ping -c 4 127.0.0.1"], "success_msg": "📡 Ping 测试完成!"},
|
||||
{"id": "l9_4_netstat", "title": "查看网络连接", "description": "使用 netstat -tlnp 查看监听端口", "hint": "netstat -tlnp", "success_test": "'LISTEN' in output or 'tcp' in output.lower()", "solution": ["netstat -tlnp"], "success_msg": "🔗 网络连接已显示!"},
|
||||
{"id": "l9_5_ss", "title": "现代方式查看端口", "description": "使用 ss -tlnp 查看监听端口", "hint": "ss -tlnp", "success_test": "cmd.startswith('ss')", "solution": ["ss -tlnp"], "success_msg": "🔗 端口信息已显示!"},
|
||||
{"id": "l9_6_curl", "title": "HTTP 请求", "description": "使用 curl 访问 http://localhost:8080", "hint": "curl http://localhost:8080", "success_test": "cmd.startswith('curl')", "solution": ["curl http://localhost:8080"], "success_msg": "🌐 HTTP 请求已发送!"},
|
||||
{"id": "l9_7_wget", "title": "下载文件", "description": "使用 wget 下载网页", "hint": "wget http://localhost:8080 -O /tmp/test.html", "success_test": "cmd.startswith('wget')", "solution": ["wget http://localhost:8080 -O /tmp/test.html"], "success_msg": "📥 文件下载命令已执行!"},
|
||||
{"id": "l9_8_traceroute", "title": "路由追踪", "description": "使用 traceroute 追踪到目标的路由", "hint": "traceroute 8.8.8.8", "success_test": "cmd.startswith('traceroute') or cmd.startswith('tracepath')", "solution": ["traceroute 8.8.8.8"], "success_msg": "🛤️ 路由追踪已启动!"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_10_package",
|
||||
"title": "📦 Level 10: 软件包管理",
|
||||
"description": "yum、apt、rpm、dpkg 包管理",
|
||||
"challenges": [
|
||||
{"id": "l10_1_yum_list", "title": "列出已安装包", "description": "使用 yum list installed 查看已安装的包", "hint": "yum list installed", "success_test": "cmd.startswith('yum')", "solution": ["yum list installed"], "success_msg": "📋 已安装包列表!"},
|
||||
{"id": "l10_2_yum_search", "title": "搜索包", "description": "使用 yum search 搜索 nginx", "hint": "yum search nginx", "success_test": "cmd.startswith('yum search')", "solution": ["yum search nginx"], "success_msg": "🔍 包搜索完成!"},
|
||||
{"id": "l10_3_rpm_q", "title": "查询包信息", "description": "使用 rpm -q 查询 bash 包", "hint": "rpm -q bash", "success_test": "cmd.startswith('rpm')", "solution": ["rpm -q bash"], "success_msg": "📦 包信息已显示!"},
|
||||
{"id": "l10_4_apt_update", "title": "更新包列表", "description": "使用 apt update 更新包列表", "hint": "apt update", "success_test": "cmd.startswith('apt update')", "solution": ["apt update"], "success_msg": "🔄 包列表已更新!"},
|
||||
{"id": "l10_5_dpkg_l", "title": "列出 Debian 包", "description": "使用 dpkg -l 列出已安装的包", "hint": "dpkg -l", "success_test": "cmd.startswith('dpkg')", "solution": ["dpkg -l"], "success_msg": "📋 Debian 包列表!"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_11_vim",
|
||||
"title": "✏️ Level 11: Vi/Vim 编辑器",
|
||||
"description": "vim 基本操作和常用命令",
|
||||
"challenges": [
|
||||
{"id": "l11_1_vim", "title": "打开文件", "description": "使用 vim 打开 /tmp/test.txt", "hint": "vim /tmp/test.txt", "success_test": "cmd.startswith('vim') or cmd.startswith('vi')", "solution": ["vim /tmp/test.txt"], "success_msg": "📝 Vim 已启动!"},
|
||||
{"id": "l11_2_vim_i", "title": "插入模式", "description": "在 vim 中按 i 进入插入模式", "hint": "按 i 键", "success_test": "cmd == 'i'", "solution": ["i"], "success_msg": "⌨️ 插入模式!"},
|
||||
{"id": "l11_3_vim_esc", "title": "退出插入模式", "description": "按 Esc 退出插入模式", "hint": "按 Esc 键", "success_test": "cmd == 'esc' or cmd == 'Esc'", "solution": ["esc"], "success_msg": "🚪 退出插入模式!"},
|
||||
{"id": "l11_4_vim_wq", "title": "保存退出", "description": "输入 :wq 保存并退出", "hint": ":wq", "success_test": "cmd == ':wq'", "solution": [":wq"], "success_msg": "💾 保存并退出!"},
|
||||
{"id": "l11_5_vim_q", "title": "不保存退出", "description": "输入 :q! 强制退出不保存", "hint": ":q!", "success_test": "cmd == ':q!'", "solution": [":q!"], "success_msg": "🚪 强制退出!"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "level_12_shell",
|
||||
"title": "🚀 Level 12: Shell 脚本编程",
|
||||
"description": "Shell 脚本基础、变量、循环、条件判断",
|
||||
"challenges": [
|
||||
{"id": "l12_1_echo_var", "title": "输出变量", "description": "使用 echo 输出环境变量 $HOME", "hint": "echo $HOME", "success_test": "'$HOME' in cmd or 'echo $' in cmd", "solution": ["echo $HOME"], "success_msg": "🔤 变量已输出!"},
|
||||
{"id": "l12_2_env", "title": "查看环境变量", "description": "使用 env 查看所有环境变量", "hint": "env", "success_test": "cmd == 'env'", "solution": ["env"], "success_msg": "🌍 环境变量已显示!"},
|
||||
{"id": "l12_3_export", "title": "设置环境变量", "description": "使用 export 设置 MYVAR=test", "hint": "export MYVAR=test", "success_test": "cmd.startswith('export')", "solution": ["export MYVAR=test"], "success_msg": "📤 环境变量已设置!"},
|
||||
{"id": "l12_4_alias", "title": "命令别名", "description": "创建别名 ll='ls -l'", "hint": "alias ll='ls -l'", "success_test": "cmd.startswith('alias')", "solution": ["alias ll='ls -l'"], "success_msg": "🏷️ 别名已创建!"},
|
||||
{"id": "l12_5_date", "title": "显示日期", "description": "使用 date 显示当前日期时间", "hint": "date", "success_test": "cmd == 'date'", "solution": ["date"], "success_msg": "📅 日期已显示!"},
|
||||
{"id": "l12_6_cal", "title": "显示日历", "description": "使用 cal 显示本月日历", "hint": "cal", "success_test": "cmd == 'cal'", "solution": ["cal"], "success_msg": "📆 日历已显示!"},
|
||||
{"id": "l12_7_bc", "title": "计算器", "description": "使用 bc 计算 1+1", "hint": "echo '1+1' | bc", "success_test": "'bc' in cmd", "solution": ["echo '1+1' | bc"], "success_msg": "🧮 计算完成!"},
|
||||
{"id": "l12_8_tar", "title": "打包压缩", "description": "将 /tmp/testdir 打包为 /tmp/testdir.tar.gz", "hint": "tar -czf /tmp/testdir.tar.gz /tmp/testdir", "success_test": "cmd.startswith('tar')", "solution": ["tar -czf /tmp/testdir.tar.gz /tmp/testdir"], "success_msg": "📦 打包完成!"},
|
||||
{"id": "l12_9_untar", "title": "解压", "description": "解压 /tmp/testdir.tar.gz 到 /tmp/extract/", "hint": "tar -xzf /tmp/testdir.tar.gz -C /tmp/extract/", "success_test": "'-x' in cmd or '--extract' in cmd", "solution": ["tar -xzf /tmp/testdir.tar.gz -C /tmp/"], "success_msg": "📂 解压完成!"},
|
||||
{"id": "l12_10_crontab", "title": "定时任务", "description": "使用 crontab -l 查看定时任务", "hint": "crontab -l", "success_test": "cmd.startswith('crontab')", "solution": ["crontab -l"], "success_msg": "⏰ 定时任务已显示!"}
|
||||
"id": "module_8_monitoring_and_scheduling",
|
||||
"title": "Module 8: Read system health and time-based execution",
|
||||
"summary": "Learn the commands that help you interpret a host snapshot and think about recurring work.",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m8_l1_system_snapshot",
|
||||
"title": "Take a quick host snapshot with uptime, free, w, and last",
|
||||
"goal": "Build a fast first-pass host summary before deeper debugging.",
|
||||
"why_it_matters": "A good operator can summarize load, memory, active sessions, and recent logins in under a minute.",
|
||||
"concepts": ["Load averages", "Memory pressure", "Active users", "Login history"],
|
||||
"command": "uptime / free / w / last",
|
||||
"examples": ["uptime", "free", "w", "last"],
|
||||
"pitfalls": ["Reading one metric in isolation", "Ignoring user/session context during troubleshooting"],
|
||||
"scenarios": ["A host feels slow but you need a broad snapshot first", "You need to know whether other users are active on the system"],
|
||||
"troubleshooting_flow": ["Check load with uptime", "Check memory with free", "Check active sessions with w", "Review recent logins with last"],
|
||||
"related_commands": ["uptime", "free", "w", "last", "top"],
|
||||
"classic_view": "Before deep investigation, collect a fast baseline of load, memory, users, and logins.",
|
||||
"takeaways": ["Use multiple quick metrics together", "Include session context in your diagnosis", "Build a snapshot before diving deeper"],
|
||||
"after_class": "Practice summarizing the host in one sentence using all four commands together.",
|
||||
"exercises": [
|
||||
{ "id": "m8_l1_e1", "type": "operation", "title": "Check uptime", "hint": "Run uptime.", "success_test": "cmd == 'uptime' and 'load average' in output", "solution": ["uptime"], "success_msg": "You read the broad load snapshot." },
|
||||
{ "id": "m8_l1_e2", "type": "operation", "title": "Check memory usage", "hint": "Run free.", "success_test": "cmd == 'free' and 'Mem:' in output", "solution": ["free"], "success_msg": "You checked memory pressure." },
|
||||
{ "id": "m8_l1_e3", "type": "operation", "title": "Check active users", "hint": "Run w.", "success_test": "cmd == 'w' and 'USER' in output", "solution": ["w"], "success_msg": "You reviewed active shell users." },
|
||||
{ "id": "m8_l1_e4", "type": "operation", "title": "Check recent login history", "hint": "Run last.", "success_test": "cmd == 'last' and 'sandbox_user' in output", "solution": ["last"], "success_msg": "You reviewed recent login history." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m8_l2_ports_and_paths",
|
||||
"title": "Inspect open paths with lsof, netstat, traceroute, and dig",
|
||||
"goal": "Use deeper network tools when the simple service or socket checks are not enough.",
|
||||
"why_it_matters": "Real network debugging often requires more than ping and curl.",
|
||||
"concepts": ["Open file and socket ownership", "Legacy vs modern network listing", "Route tracing", "DNS lookup"],
|
||||
"command": "lsof / netstat / traceroute / dig",
|
||||
"examples": ["lsof", "netstat", "traceroute 8.8.8.8", "dig example.com"],
|
||||
"pitfalls": ["Treating all network questions as port-only problems", "Ignoring route or DNS evidence when connectivity looks inconsistent"],
|
||||
"scenarios": ["Confirm which process owns a listening socket", "Trace whether the path to a remote target looks reachable", "Check if DNS is the real failure point"],
|
||||
"troubleshooting_flow": ["Use lsof or netstat to inspect local ownership", "Use traceroute to reason about path", "Use dig to isolate DNS problems"],
|
||||
"related_commands": ["lsof", "netstat", "traceroute", "dig", "ss"],
|
||||
"classic_view": "When simple checks stop being enough, move to ownership, route, and DNS visibility.",
|
||||
"takeaways": ["Use lsof for ownership clues", "Use traceroute for route intuition", "Use dig to separate DNS from transport issues"],
|
||||
"after_class": "Practice describing which question each command answers before you run it.",
|
||||
"exercises": [
|
||||
{ "id": "m8_l2_e1", "type": "operation", "title": "Inspect open files and sockets", "hint": "Run lsof.", "success_test": "cmd == 'lsof' and 'LISTEN' in output", "solution": ["lsof"], "success_msg": "You inspected file and socket ownership." },
|
||||
{ "id": "m8_l2_e2", "type": "operation", "title": "Inspect listening network state", "hint": "Run netstat.", "success_test": "cmd == 'netstat' and 'LISTEN' in output", "solution": ["netstat"], "success_msg": "You inspected network listening state with netstat." },
|
||||
{ "id": "m8_l2_e3", "type": "operation", "title": "Trace a network path", "hint": "Run traceroute 8.8.8.8.", "success_test": "cmd == 'traceroute 8.8.8.8' and 'traceroute' in output", "solution": ["traceroute 8.8.8.8"], "success_msg": "You traced a network path." },
|
||||
{ "id": "m8_l2_e4", "type": "operation", "title": "Check DNS resolution", "hint": "Run dig example.com.", "success_test": "cmd == 'dig example.com' and 'ANSWER SECTION' in output", "solution": ["dig example.com"], "success_msg": "You separated DNS lookup from the rest of the network path." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m8_l3_time_and_schedule",
|
||||
"title": "Read scheduling context with date, cal, and crontab",
|
||||
"goal": "Think about when work runs, not just what runs.",
|
||||
"why_it_matters": "A lot of operations work is time-based: jobs, cutovers, maintenance windows, and recurring checks.",
|
||||
"concepts": ["Current system time", "Calendar context", "Recurring jobs"],
|
||||
"command": "date / cal / crontab",
|
||||
"examples": ["date", "cal", "crontab -l"],
|
||||
"pitfalls": ["Ignoring system time when reading logs or scheduling changes", "Treating cron like magic instead of planned automation"],
|
||||
"scenarios": ["Confirm the current host time before correlating logs", "Check whether a user has scheduled jobs", "Think about maintenance windows"],
|
||||
"troubleshooting_flow": ["Read current time first", "Check the calendar context", "Inspect scheduled jobs before assuming nothing runs automatically"],
|
||||
"related_commands": ["date", "cal", "crontab", "history"],
|
||||
"classic_view": "Time is part of system state, and operations mistakes often come from forgetting that.",
|
||||
"takeaways": ["Time-stamp your thinking", "Use calendar context during planning", "Check cron before assuming a task is manual"],
|
||||
"after_class": "Explain how wrong time assumptions can distort a log or incident timeline.",
|
||||
"exercises": [
|
||||
{ "id": "m8_l3_e1", "type": "operation", "title": "Read the current system time", "hint": "Run date.", "success_test": "cmd == 'date' and 'CST' in output", "solution": ["date"], "success_msg": "You checked the system time." },
|
||||
{ "id": "m8_l3_e2", "type": "operation", "title": "Open the calendar view", "hint": "Run cal.", "success_test": "cmd == 'cal' and 'March' in output", "solution": ["cal"], "success_msg": "You opened a calendar view for planning context." },
|
||||
{ "id": "m8_l3_e3", "type": "operation", "title": "Inspect scheduled jobs", "hint": "Run crontab -l.", "success_test": "cmd == 'crontab -l' and 'crontab' in output", "solution": ["crontab -l"], "success_msg": "You checked for scheduled cron jobs." },
|
||||
{ "id": "m8_l3_e4", "type": "scenario", "question": "Why should you check date before correlating logs from an incident?", "answer": "Because a wrong time assumption can make the event sequence look incorrect even if the commands are right." }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
1761
index.html
1761
index.html
File diff suppressed because it is too large
Load Diff
92
privacy.html
Normal file
92
privacy.html
Normal file
@@ -0,0 +1,92 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Linux Learning Lab Privacy Notice</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f4f7fb;
|
||||
--panel: #ffffff;
|
||||
--line: #d8e2ee;
|
||||
--text: #122238;
|
||||
--muted: #5b6f86;
|
||||
--brand: #0f6db6;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
font-family: "Aptos", "Segoe UI", "Microsoft YaHei", sans-serif;
|
||||
background: linear-gradient(180deg, #f4f7fb 0%, #eaf0f7 100%);
|
||||
color: var(--text);
|
||||
line-height: 1.85;
|
||||
}
|
||||
main {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
background: rgba(255,255,255,0.94);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 24px;
|
||||
padding: 28px;
|
||||
box-shadow: 0 18px 40px rgba(16, 32, 51, 0.10);
|
||||
}
|
||||
h1, h2 { margin-top: 0; }
|
||||
h1 { font-size: 30px; margin-bottom: 8px; }
|
||||
h2 { margin: 26px 0 10px; font-size: 20px; }
|
||||
.eyebrow {
|
||||
color: var(--brand);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.muted { color: var(--muted); }
|
||||
ul { margin: 0; padding-left: 20px; }
|
||||
code {
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
background: #eef5ff;
|
||||
color: var(--brand);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="eyebrow">Privacy Notice</div>
|
||||
<h1>Linux Learning Lab Privacy Notice</h1>
|
||||
<p class="muted">Last updated: 2026-03-18</p>
|
||||
|
||||
<p>This page applies to the Linux Learning Lab web interface, lesson search endpoints, and sandbox command simulation features. The platform is designed to teach Linux command usage, troubleshooting flow, and common operations scenarios.</p>
|
||||
|
||||
<h2>1. What data the platform processes</h2>
|
||||
<ul>
|
||||
<li>Search queries, login details, and commands that you intentionally submit.</li>
|
||||
<li>Sandbox runtime state used to return a response, such as current directory, current user, and simulated command output.</li>
|
||||
<li>Minimal operational logs used for debugging and service reliability.</li>
|
||||
</ul>
|
||||
|
||||
<h2>2. Why the data is used</h2>
|
||||
<ul>
|
||||
<li>To render lesson content, search results, module summaries, and guided exercises.</li>
|
||||
<li>To execute the controlled Linux sandbox simulation and return exercise feedback.</li>
|
||||
<li>To protect remote endpoints with authentication and basic abuse prevention.</li>
|
||||
</ul>
|
||||
|
||||
<h2>3. Sandbox and safety boundary</h2>
|
||||
<p>The command interface is a controlled simulation, not a direct pass-through to a real operating system. The platform blocks clearly unsafe inputs such as destructive shell patterns. Even so, the sandbox endpoints such as <code>/api/run</code> should only be used for learning and testing purposes.</p>
|
||||
|
||||
<h2>4. Retention and sharing</h2>
|
||||
<p>The platform does not sell personal data. Runtime logs may be kept for a limited period to support debugging and service maintenance. If the platform is hosted behind a proxy or third-party provider, that infrastructure may also process request metadata such as IP address, timestamp, and path.</p>
|
||||
|
||||
<h2>5. Your controls</h2>
|
||||
<ul>
|
||||
<li>You can stop using the platform at any time.</li>
|
||||
<li>You can reset the sandbox to clear the current simulated file system state.</li>
|
||||
<li>If you need local data removed, contact the current deployer or repository maintainer.</li>
|
||||
</ul>
|
||||
|
||||
<h2>6. Contact and maintenance</h2>
|
||||
<p>If you have questions about privacy, logging, or sandbox safety, contact the current service owner. For self-hosted deployments, review the web server, proxy, and hosting platform logging settings alongside this application.</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
1268
sandbox.py
1268
sandbox.py
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user