feat: redesign linux course around learning-first structure
This commit is contained in:
@@ -1,191 +1,284 @@
|
||||
{
|
||||
"meta": {
|
||||
"version": "3.0",
|
||||
"title": "Linux 运维工程师完整教程",
|
||||
"author": "PMClaw",
|
||||
"updated": "2026-03-06",
|
||||
"description": "覆盖菜鸟教程 Linux 全部内容,从入门到精通",
|
||||
"total_levels": 12,
|
||||
"total_challenges": 80
|
||||
"version": "4.0",
|
||||
"title": "Linux 系统学习课程(重构版)",
|
||||
"author": "OpenClaw Dev",
|
||||
"updated": "2026-03-10",
|
||||
"description": "强调知识理解、场景迁移与轻量练习的 Linux 学习课程",
|
||||
"module_count": 6,
|
||||
"total_lessons": 18,
|
||||
"total_exercises": 54
|
||||
},
|
||||
"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": "模块 1:建立 Linux 基本认知",
|
||||
"summary": "先理解终端、目录、路径和最基础命令,建立 Linux 使用的空间感。",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m1_l1_pwd",
|
||||
"title": "认识当前目录:pwd",
|
||||
"goal": "理解当前工作目录的意义,知道自己在文件系统中的位置。",
|
||||
"why_it_matters": "很多 Linux 操作依赖路径。如果不知道自己当前在哪,后续命令容易出错。",
|
||||
"concepts": [
|
||||
"当前工作目录",
|
||||
"绝对路径与相对路径",
|
||||
"为什么要先定位再操作"
|
||||
],
|
||||
"command": "pwd",
|
||||
"examples": [
|
||||
"pwd",
|
||||
"cd /tmp && pwd"
|
||||
],
|
||||
"pitfalls": [
|
||||
"以为终端默认总在同一个目录",
|
||||
"不分清当前目录和目标目录"
|
||||
],
|
||||
"scenarios": [
|
||||
"切目录后确认自己到了哪里",
|
||||
"写脚本前确认当前运行位置"
|
||||
],
|
||||
"exercises": [
|
||||
{
|
||||
"id": "m1_l1_e1",
|
||||
"type": "understanding",
|
||||
"question": "查看当前工作目录应该使用什么命令?",
|
||||
"answer": "pwd"
|
||||
},
|
||||
{
|
||||
"id": "m1_l1_e2",
|
||||
"type": "operation",
|
||||
"title": "输出当前目录",
|
||||
"hint": "直接输入 pwd",
|
||||
"success_test": "cmd == 'pwd'",
|
||||
"solution": ["pwd"],
|
||||
"success_msg": "你已经能确认自己所在的位置了。"
|
||||
},
|
||||
{
|
||||
"id": "m1_l1_e3",
|
||||
"type": "scenario",
|
||||
"question": "如果你不确定自己当前在哪个目录,第一反应应该做什么?",
|
||||
"answer": "先执行 pwd 确认当前目录"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m1_l2_ls",
|
||||
"title": "看见目录内容:ls",
|
||||
"goal": "理解 ls 的作用,并掌握查看隐藏文件和详细信息的基本方式。",
|
||||
"why_it_matters": "Linux 下很多探索行为都从 ls 开始,它决定你如何观察目录结构。",
|
||||
"concepts": [
|
||||
"目录内容查看",
|
||||
"隐藏文件",
|
||||
"长列表信息"
|
||||
],
|
||||
"command": "ls",
|
||||
"examples": [
|
||||
"ls",
|
||||
"ls -la",
|
||||
"ls -lh /etc"
|
||||
],
|
||||
"pitfalls": [
|
||||
"误以为 ls 看不到的文件就不存在",
|
||||
"不会区分普通 ls 和 ls -l 的用途"
|
||||
],
|
||||
"scenarios": [
|
||||
"排查目录里到底有哪些文件",
|
||||
"检查配置目录中是否有隐藏文件"
|
||||
],
|
||||
"exercises": [
|
||||
{
|
||||
"id": "m1_l2_e1",
|
||||
"type": "understanding",
|
||||
"question": "为什么 ls -a 会比 ls 多看到一些文件?",
|
||||
"answer": "因为它会显示隐藏文件,包括以点开头的文件"
|
||||
},
|
||||
{
|
||||
"id": "m1_l2_e2",
|
||||
"type": "operation",
|
||||
"title": "列出当前目录内容",
|
||||
"hint": "输入 ls",
|
||||
"success_test": "cmd == 'ls'",
|
||||
"solution": ["ls"],
|
||||
"success_msg": "你已经会观察目录内容了。"
|
||||
},
|
||||
{
|
||||
"id": "m1_l2_e3",
|
||||
"type": "operation",
|
||||
"title": "显示隐藏文件和详细信息",
|
||||
"hint": "使用 ls -la",
|
||||
"success_test": "cmd == 'ls -la' or cmd == 'ls -al'",
|
||||
"solution": ["ls -la", "ls -al"],
|
||||
"success_msg": "你已经会用更完整的方式查看目录了。"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m1_l3_cd_cat_echo",
|
||||
"title": "移动、读文件、输出文本",
|
||||
"goal": "掌握 cd、cat、echo 这些最基础但最常用的命令。",
|
||||
"why_it_matters": "这三个命令几乎贯穿 Linux 入门阶段的所有练习。",
|
||||
"concepts": [
|
||||
"切换目录",
|
||||
"读取文件",
|
||||
"输出文本与变量"
|
||||
],
|
||||
"command": "cd / cat / echo",
|
||||
"examples": [
|
||||
"cd /tmp",
|
||||
"cat /etc/hosts",
|
||||
"echo Hello Linux"
|
||||
],
|
||||
"pitfalls": [
|
||||
"把 cd 和 ls 混用",
|
||||
"用 cat 去看过大的文件",
|
||||
"不知道 echo 也常用于脚本调试"
|
||||
],
|
||||
"scenarios": [
|
||||
"进入指定目录继续操作",
|
||||
"快速读取配置文件",
|
||||
"验证变量和命令输出"
|
||||
],
|
||||
"exercises": [
|
||||
{
|
||||
"id": "m1_l3_e1",
|
||||
"type": "operation",
|
||||
"title": "进入 /tmp 目录",
|
||||
"hint": "cd /tmp",
|
||||
"success_test": "cmd == 'cd /tmp' and cwd == '/tmp'",
|
||||
"solution": ["cd /tmp"],
|
||||
"success_msg": "你已经能切换到目标目录了。"
|
||||
},
|
||||
{
|
||||
"id": "m1_l3_e2",
|
||||
"type": "operation",
|
||||
"title": "读取 hosts 文件",
|
||||
"hint": "cat /etc/hosts",
|
||||
"success_test": "cmd == 'cat /etc/hosts' and 'localhost' in output",
|
||||
"solution": ["cat /etc/hosts"],
|
||||
"success_msg": "你已经会读取基础文本文件了。"
|
||||
},
|
||||
{
|
||||
"id": "m1_l3_e3",
|
||||
"type": "operation",
|
||||
"title": "输出 Hello Linux",
|
||||
"hint": "echo Hello Linux",
|
||||
"success_test": "cmd == 'echo Hello Linux' and 'Hello Linux' in output",
|
||||
"solution": ["echo Hello Linux"],
|
||||
"success_msg": "你已经掌握了最基础的文本输出命令。"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "模块 2:文件与目录操作",
|
||||
"summary": "围绕创建、复制、移动、删除和查看文件属性建立文件系统操作能力。",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m2_l1_create",
|
||||
"title": "创建文件与目录:mkdir / touch",
|
||||
"goal": "理解目录和文件的创建逻辑,学会递归创建多级目录。",
|
||||
"why_it_matters": "很多项目初始化、环境准备都从创建目录结构开始。",
|
||||
"concepts": ["目录创建", "多级目录", "空文件创建"],
|
||||
"command": "mkdir / touch",
|
||||
"examples": ["mkdir demo", "mkdir -p /tmp/a/b/c", "touch notes.txt"],
|
||||
"pitfalls": ["忘记使用 -p 创建多级目录", "目标父目录不存在时 touch 失败"],
|
||||
"scenarios": ["初始化项目目录结构", "创建占位文件和日志文件"],
|
||||
"exercises": [
|
||||
{"id": "m2_l1_e1", "type": "operation", "title": "递归创建目录", "hint": "mkdir -p /tmp/a/b/c", "success_test": "cmd == 'mkdir -p /tmp/a/b/c' and exists('/tmp/a/b/c')", "solution": ["mkdir -p /tmp/a/b/c"], "success_msg": "多级目录创建成功。"},
|
||||
{"id": "m2_l1_e2", "type": "operation", "title": "创建空文件", "hint": "touch /tmp/a/b/c/readme.txt", "success_test": "cmd == 'touch /tmp/a/b/c/readme.txt' and exists('/tmp/a/b/c/readme.txt')", "solution": ["touch /tmp/a/b/c/readme.txt"], "success_msg": "空文件创建成功。"},
|
||||
{"id": "m2_l1_e3", "type": "scenario", "question": "为什么 mkdir -p 适合项目初始化?", "answer": "因为它可以一次创建多级目录,即使上层目录不存在也能自动补齐"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m2_l2_move_copy_delete",
|
||||
"title": "复制、移动与删除:cp / mv / rm",
|
||||
"goal": "理解文件操作中的备份、迁移、重命名和清理。",
|
||||
"why_it_matters": "日常 Linux 使用里最常见的就是处理文件的生命周期。",
|
||||
"concepts": ["复制与备份", "移动与重命名", "删除风险"],
|
||||
"command": "cp / mv / rm",
|
||||
"examples": ["cp /etc/hosts /tmp/hosts.bak", "mv old.txt new.txt", "rm -r /tmp/testdir"],
|
||||
"pitfalls": ["把删除当成移动", "对目录使用 cp 却忘记 -r", "rm -rf 风险极高"],
|
||||
"scenarios": ["做配置备份", "整理日志文件", "清理无用目录"],
|
||||
"exercises": [
|
||||
{"id": "m2_l2_e1", "type": "operation", "title": "复制 hosts 文件", "hint": "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": "文件备份成功。"},
|
||||
{"id": "m2_l2_e2", "type": "operation", "title": "重命名备份文件", "hint": "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": "文件重命名成功。"},
|
||||
{"id": "m2_l2_e3", "type": "understanding", "question": "为什么 rm -rf 是高风险命令?", "answer": "因为它会递归并强制删除文件和目录,执行错误会造成不可恢复的数据丢失"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m2_l3_stat_permissions",
|
||||
"title": "认识文件属性:stat 与权限基础",
|
||||
"goal": "开始理解文件属性和权限表达。",
|
||||
"why_it_matters": "文件权限是 Linux 系统安全和协作的重要基础。",
|
||||
"concepts": ["文件元信息", "权限三元组", "目录与文件权限差异"],
|
||||
"command": "stat / chmod",
|
||||
"examples": ["stat /etc/hosts", "chmod 755 script.sh", "chmod +x run.sh"],
|
||||
"pitfalls": ["不了解 755 / 644 的含义", "给不该执行的文件随意加执行权限"],
|
||||
"scenarios": ["检查脚本是否可执行", "排查权限导致的运行失败"],
|
||||
"exercises": [
|
||||
{"id": "m2_l3_e1", "type": "operation", "title": "查看 hosts 属性", "hint": "stat /etc/hosts", "success_test": "cmd == 'stat /etc/hosts' and 'File:' in output", "solution": ["stat /etc/hosts"], "success_msg": "你已经会查看文件属性了。"},
|
||||
{"id": "m2_l3_e2", "type": "understanding", "question": "755 和 644 最核心的区别是什么?", "answer": "755 允许拥有者读写执行,其他人读执行;644 没有执行权限"},
|
||||
{"id": "m2_l3_e3", "type": "operation", "title": "给文件添加执行权限", "hint": "chmod +x /tmp/a/b/c/readme.txt", "success_test": "cmd == 'chmod +x /tmp/a/b/c/readme.txt'", "solution": ["chmod +x /tmp/a/b/c/readme.txt"], "success_msg": "你已经完成了权限修改练习。"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "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": "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": "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": "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": "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_3_searching",
|
||||
"title": "模块 3:阅读与筛选信息",
|
||||
"summary": "把 Linux 当成信息检索工具来学,围绕日志、配置和统计建立阅读能力。",
|
||||
"lessons": [
|
||||
{
|
||||
"id": "m3_l1_read_logs",
|
||||
"title": "看文件头尾:head / tail",
|
||||
"goal": "学会快速读取大文件的局部内容。",
|
||||
"why_it_matters": "日志通常很大,不可能总是整份去看。",
|
||||
"concepts": ["查看前几行", "查看后几行", "实时追踪"],
|
||||
"command": "head / tail",
|
||||
"examples": ["head -n 5 /var/log/syslog", "tail -n 20 /var/log/syslog", "tail -f /var/log/syslog"],
|
||||
"pitfalls": ["大文件直接 cat 影响阅读效率", "不会区分查看历史和跟踪新增日志"],
|
||||
"scenarios": ["看配置文件开头", "盯日志尾部排查实时错误"],
|
||||
"exercises": [
|
||||
{"id": "m3_l1_e1", "type": "operation", "title": "查看 syslog 前 5 行", "hint": "head -n 5 /var/log/syslog", "success_test": "(cmd == 'head -n 5 /var/log/syslog' or cmd == 'head -5 /var/log/syslog') and len(output.split('\n')) >= 5", "solution": ["head -n 5 /var/log/syslog", "head -5 /var/log/syslog"], "success_msg": "你已经会局部查看大文件开头了。"},
|
||||
{"id": "m3_l1_e2", "type": "operation", "title": "查看 syslog 最后 3 行", "hint": "tail -n 3 /var/log/syslog", "success_test": "(cmd == 'tail -n 3 /var/log/syslog' or cmd == 'tail -3 /var/log/syslog') and len(output.split('\n')) >= 3", "solution": ["tail -n 3 /var/log/syslog", "tail -3 /var/log/syslog"], "success_msg": "你已经会快速查看日志尾部了。"},
|
||||
{"id": "m3_l1_e3", "type": "scenario", "question": "为什么排查线上报错时更常先用 tail 而不是 cat?", "answer": "因为日志通常很大,tail 可以更快聚焦最近发生的问题"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m3_l2_grep",
|
||||
"title": "关键词搜索:grep",
|
||||
"goal": "理解 grep 作为日志排障和文本定位核心工具的价值。",
|
||||
"why_it_matters": "没有 grep,查日志和配置会慢很多。",
|
||||
"concepts": ["大小写忽略", "显示行号", "反向匹配", "递归搜索"],
|
||||
"command": "grep",
|
||||
"examples": ["grep error /var/log/syslog", "grep -in root /etc/passwd", "grep -v nologin /etc/passwd"],
|
||||
"pitfalls": ["不会结合 -n 定位行号", "不知道 -i 和 -v 的常见用途"],
|
||||
"scenarios": ["查错误日志", "找配置项", "过滤无效行"],
|
||||
"exercises": [
|
||||
{"id": "m3_l2_e1", "type": "operation", "title": "查找 syslog 中的 error", "hint": "grep error /var/log/syslog", "success_test": "cmd == 'grep error /var/log/syslog' and 'error' in output.lower()", "solution": ["grep error /var/log/syslog"], "success_msg": "你已经会在日志里搜关键词了。"},
|
||||
{"id": "m3_l2_e2", "type": "operation", "title": "忽略大小写搜索 root", "hint": "grep -i root /etc/passwd", "success_test": "cmd == 'grep -i root /etc/passwd'", "solution": ["grep -i root /etc/passwd"], "success_msg": "你已经知道如何处理大小写差异了。"},
|
||||
{"id": "m3_l2_e3", "type": "understanding", "question": "grep -n 的意义是什么?", "answer": "显示匹配结果所在的行号,方便快速定位原文位置"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "m3_l3_find_wc_sort",
|
||||
"title": "查找与统计:find / wc / sort",
|
||||
"goal": "建立查找文件和做基础统计的能力。",
|
||||
"why_it_matters": "Linux 的很多效率来自组合式查找与统计。",
|
||||
"concepts": ["按名称查找", "行数字数统计", "排序输出"],
|
||||
"command": "find / wc / sort",
|
||||
"examples": ["find /etc -name '*.conf'", "wc -l /var/log/syslog", "ls | sort"],
|
||||
"pitfalls": ["把 find 和 grep 混淆", "不会根据任务选文件查找还是内容查找"],
|
||||
"scenarios": ["找配置文件", "统计日志行数", "整理输出结果"],
|
||||
"exercises": [
|
||||
{"id": "m3_l3_e1", "type": "operation", "title": "查找 /etc 下所有 .conf 文件", "hint": "find /etc -name '*.conf'", "success_test": "cmd == \"find /etc -name '*.conf'\" and '.conf' in output", "solution": ["find /etc -name '*.conf'"], "success_msg": "你已经会用 find 定位文件了。"},
|
||||
{"id": "m3_l3_e2", "type": "operation", "title": "统计 syslog 行数", "hint": "wc -l /var/log/syslog", "success_test": "cmd == 'wc -l /var/log/syslog' and output.strip().isdigit()", "solution": ["wc -l /var/log/syslog"], "success_msg": "你已经会做基础统计了。"},
|
||||
{"id": "m3_l3_e3", "type": "understanding", "question": "找文件位置应该优先想到 find 还是 grep?为什么?", "answer": "优先用 find,因为这是文件定位问题,不是文件内容搜索问题"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user