Skip to content

RUCICS/ShellLab-2025

Repository files navigation

ShellLab:构建 Unix Shell 🐚

1. 实验目标 🎯

通过编写一个支持作业控制的 Unix Shell 程序,深入理解进程控制和信号处理的核心概念。

2. 实验概述 📝

Shell 是一个交互式命令行解释器,负责接收用户指令并执行相应程序。本实验要求实现一个简化版的 Shell,包含基本的命令解析、进程创建、信号处理、作业控制和I/O重定向功能。实验提供了骨架代码和完整的命令解析器,你需要实现核心执行逻辑和控制机制。

关于 Shell 原理和实现方法的详细指导,请参阅实验指导文档

3. 实验要求 📋

3.1 基本功能(必做)

  • 命令提示符为 "tsh> "
  • 执行命令行程序(前台或后台运行)
  • 实现作业控制(bg、fg 等)
  • 处理键盘信号(Ctrl+C、Ctrl+Z)
  • 实现基本的 I/O 重定向(<>
  • 回收终止的子进程

3.2 内建命令

  • quit/exit:终止 shell
  • jobs:列出所有后台作业
  • bg <job>:将一个停止状态的后台作业转为运行状态
  • fg <job>:将一个后台作业转为前台运行
  • cd <dir>:切换当前工作目录

3.3 输出格式规范 📋

为了确保自动化测试的正确性,你的 Shell 必须严格遵循以下输出格式:

作业状态消息

  • 后台作业启动:[%d] (%d) %s &

    • 例如:[1] (12345) /bin/ls -l &
  • 作业被信号停止:Job [%d] (%d) stopped by signal %d

    • 例如:Job [1] (12345) stopped by signal 20
  • 作业被信号终止:Job [%d] (%d) terminated by signal %d

    • 例如:Job [1] (12345) terminated by signal 2

jobs 命令输出

  • 运行中的作业:[%d] (%d) Running %s

    • 例如:[1] (12345) Running /bin/ls -l &
  • 停止的作业:[%d] (%d) Stopped %s

    • 例如:[1] (12345) Stopped /bin/ls -l

bg/fg 命令输出

  • bg 命令输出:[%d] (%d) %s &

    • 例如:[1] (12345) /bin/ls -l &
  • fg 命令输出:不需要特殊输出,只显示命令本身

错误消息

  • 命令未找到:%s: Command not found.

    • 例如:foo: Command not found.
  • 未指定作业:%s command requires PID or %%jobid argument

    • 例如:bg command requires PID or %jobid argument
  • bg/fg 参数格式不合法:%s: argument must be a PID or %%jobid

    • 例如:bg: argument must be a PID or %jobid
  • 进程不存在:(%d): No such process

    • 例如:(12345): No such process
  • 作业 ID 不存在:%%%d: No such job

    • 例如:%1: No such job

3.4 附加任务(选做)🌟

本实验提供多项选做任务,你需要任选两项完成:

  1. 基本管道支持 📊
  • 实现单级管道连接(如 cmd1 | cmd2
  • 相关函数:在 shell.ceval 函数中处理 command_tnext 字段
  1. 高级管道支持 📈
  • 实现多级管道链(如 cmd1 | cmd2 | cmd3 | ...
  • 需要先完成基本管道支持
  1. 环境变量展开 🔄
  • 实现 $VAR${VAR} 格式的环境变量替换
  • 相关函数:在 parser.c 中实现 env_expand 函数
  1. 命令替换功能 🔁
  • 实现 $(command) 格式的命令输出替换
  • 相关函数:在 parser.c 中实现 command_substitute 函数
  1. 终端控制机制 💻
  • 实现对终端的完整控制,支持运行 vim、gdb 等交互式程序
  • 实现要点:正确管理进程组、控制终端所有权、终端属性设置
  • 相关函数:在 shell.csignals.c 中添加终端控制相关代码
  1. PATH 环境变量支持 🔍
  • 实现通过 PATH 环境变量查找可执行文件
  • 实现要点:不需要输入完整路径即可执行在 PATH 中的程序
  1. 脚本执行功能 📜
  • 支持从文件读取并执行一系列 Shell 命令,只需实现顺序执行
  • 相关函数:在 shell.c 中实现 eval_script 函数
  1. 环境变量支持(export) 🔧
  • 实现 export VAR=value 内建命令,支持在 shell 运行期间设置环境变量
  • 设置的环境变量应当被子进程继承
  • 相关函数:在 builtins.c 中添加 export 命令支持
  1. 子 Shell(基础) 🐣
  • 支持通过语法 (...) 启动一个子 Shell,并在子进程中顺序执行其中的命令
  • 子 Shell 中的环境修改(如 cd、设置变量等)不影响父 Shell
  • 实现要点:在命令解析阶段识别 (...) 结构;在执行阶段,使用 fork() 创建新进程来执行子 Shell 命令序列
  1. 子 Shell(高级) 🚀
  • 在基础子 Shell 的实现上,进一步支持子 Shell 作业控制、与父 Shell 管道连接等场景
  • 需要更深入地管理进程组、终端控制与信号,使子 Shell 在前台/后台模式下都能正确处理信号
  • 可探索让子 Shell 与父 Shell 共享部分状态(如环境变量)或实现更灵活的隔离

每项选做任务的详细要求及参考实现方法可参阅实验指导文档

4. 文件结构与接口 🗂️

项目采用模块化设计,文件结构如下:

.
├── include
│   └── shell.h     # 头文件,包含函数声明与数据结构
├── Makefile        # 用于编译项目
├── src
│   ├── builtins.c  # 内置命令的实现
│   ├── jobs.c      # 作业控制相关函数
│   ├── main.c      # 主函数
│   ├── parser.c    # 命令解析相关函数
│   ├── shell.c     # Shell 核心逻辑
│   ├── signals.c   # 信号处理函数
│   └── utils.c     # 工具函数
├── tests           # 测试用例目录
│   └── cases       # 包含多个测试点
└── tshref          # 参考实现

Note

提供的代码框架更像是一个起步代码(starter code),如果你觉得现有框架限制了你的实现方式,可以根据需要直接修改代码结构,只要最终实现满足功能要求且能通过测试即可。

命令解析器提供了以下结构:

typedef struct command {
  char *argv[MAXARGS];  /* 命令和参数 */
  int argc;             /* 参数数量 */
  char *infile;         /* 输入重定向文件 */
  char *outfile;        /* 输出重定向文件 */
  int append;           /* 输出重定向的追加模式标志 */
  struct command *next; /* 管道:指向下一个命令 */
} command_t;

int parse_command_line(const char *cmdline, command_t **cmd, int *bg);

必做任务需要实现的主要函数:

  • builtins.c: builtin_cmd, do_bgfg, do_cd
  • shell.c: eval
  • signals.c: sigchld_handler, sigint_handler, sigtstp_handler

5. 测试方法 🧪

本实验提供了一个自动评测脚本,可以帮助你验证 Shell 实现的正确性。脚本支持多种测试模式,能够模拟用户输入、检查程序输出并提供详细反馈。

python grader.py           # 运行所有测试
python grader.py 5-jobs    # 只运行特定测试点

我们还提供了一个参考实现的二进制可执行文件,你可以用它来对比你的实现。

./tshref

查看测试内容 🔍

如果想了解每个测试点具体执行的内容,可以运行:

# 查看测试会执行哪些命令但不实际运行
python grader.py -d 5-jobs

你也可以直接查看测试点目录内容,每个测试点包含一个 config.toml 配置文件,描述了测试的元数据和执行步骤。

实用调试功能 🐛

这些功能可能也会对你有所帮助:

  • 详细输出:使用 -v 参数查看每个测试步骤的详细输出,包括程序的所有输出和错误信息:
    python grader.py -v 6-sigint
  • 对比参考实现:使用 --compare 参数将你的Shell输出与参考实现进行对比:
    python grader.py --compare 5-jobs
    脚本会显示两者输出的差异,帮你识别输出格式问题或功能缺陷。
  • 重新运行失败的测试:实现复杂功能时,可以先修复一部分问题,然后专注于剩余的失败测试:
    python grader.py -f
  • 生成VS Code调试配置
    python grader.py --vscode
    这将为失败的测试生成VS Code调试配置,让你能够在调试器中步进执行代码,观察变量值和程序状态。

6. 评分标准 💯

6.1 评分构成

实验总分由以下三个部分组成:

  1. 功能正确性(50%)
  • 评分依据:自动化测试通过率
  • 计算公式:$50 \times \frac{\text{测试得分}}{\text{测试总分}}$
  1. 实现方式与扩展功能(20%)
  • 满分条件(二选一):
    • 使用 Rust 语言完成基础功能实现
    • 或使用 C 语言实现并完成至少两项选做任务
  • 评分方法:
    • Rust 实现:基础功能完整即可获得全部分数
    • C 语言实现:每完成一项选做任务得 10 分,上限 20 分
  1. 实验报告与代码质量(30%)
  • 实验报告评分要点(15%):
    • 实现思路清晰完整
    • 关键功能设计与分析深入
    • 测试结果分析全面
    • 问题解决过程与经验总结
  • 代码质量评分要点(15%):
    • 代码结构设计合理
    • 命名规范与注释完善
    • 错误处理机制健全
    • 实现的技术深度与创新性

6.2 实验评优机制 🏆

为了鼓励同学们追求卓越,本实验将对成绩排名前三的优秀实现进行特别奖励:

评优标准将基于实验总评分,包含以下所有评分维度:

  • 自动化测试的功能正确性(50%)
  • 实现方式与扩展功能完成情况(20%)
  • 实验报告质量与代码设计水平(30%)

获选的优秀实现将:

  1. 进行公示表彰
  2. 在征得作者同意后开源,作为学习参考资源
  3. 为作者提供平时成绩 5 分附加分作为奖励

参与评优无需额外申请,所有按时提交且功能完整的作业均自动纳入评选范围。我们期待看到你精心打造的高质量 Shell 实现,展现你对操作系统概念的深刻理解与应用能力。

6.3 学术诚信 🔍

我们高度重视学术诚信,它是计算机科学教育的基石。我们期望你能够独立完成实验,真正掌握系统编程的核心概念和技能。

  • 鼓励的行为

    • 与同学讨论实验的概念性问题和整体设计思路
    • 在课堂或实验课上请教助教关于实验中遇到的困难
    • 查阅官方文档、教科书和其他公开学习资源
  • 禁止的行为

    • 抄袭或共享代码(包括但不限于同学之间、网络资源等)
    • 试图绕过或破解测试系统
    • 让他人代为完成作业

Warning

我们使用代码相似度检测工具对所有提交的代码进行检查。一旦发现抄袭或其他学术不端行为,将严格按照学校相关规定处理。

6.4 关于 AI 工具使用 🤖

在现代编程环境中,ChatGPT、Claude、DeepSeek 等大型语言模型以及基于它们的工具(如 GitHub Copilot、Cursor 等)已成为许多开发者的辅助工具。我们认可这些工具在学习过程中的价值,同时也希望你合理使用它们:

  1. 理解优先:AI 工具可以帮助解释概念、提供思路或优化代码片段,但不应替代你对底层原理的理解。使用这些工具前,请先尝试自己分析问题。
  2. 学习而非依赖:将 AI 工具视为学习助手而非解决方案提供者。如果使用 AI 生成代码,确保你完全理解每一行代码的作用及其背后的原理。同时,使用 AI 工具直接生成完整的实验解决方案并提交为自己的工作是不允许的。这不仅违背了学术诚信原则,也会阻碍你获得通过实验设计的学习体验。
  3. 批判性思考:AI 生成的内容可能存在错误或不适合特定场景。建议始终以批判性思维评估其建议,并根据你对操作系统概念的理解进行调整。
  4. 在报告中声明:如果你在实验过程中使用了 AI 工具获取重要帮助,建议在实验报告中简要说明使用方式和范围。

合理使用AI工具可以增强学习效果,但最终的理解和代码实现应反映你自己的努力和思考。操作系统是计算机科学的核心领域,亲自实现这些机制将为你的技术成长奠定坚实基础。

7. 提交方式 📤

使用 GitHub Classroom 进行提交。请你确保所有代码已提交到你的对应仓库,GitHub Actions 会自动运行测试,其输出作为我们的评分依据。

提交截止日期:2025 年 3 月 21 日 23:59

提交内容:

  1. 所有源代码文件(通过 GitHub 仓库提交)
  2. 实验报告(需同时提交源文件转换后的 PDF,详细要求请参阅报告模板与要求

请确保在截止日期前完成最终提交。GitHub 会记录你的所有提交历史,我们将以截止日期前的最后一次提交作为最终版本进行评分。

8. 探索方向 🚀

完成基础实验后,你可能会对 Shell 的原理和实现产生更浓厚的兴趣。本节提供一些探索方向,帮助你将实验 Shell 逐步打磨成一款真正可用的现代 Shell 工具。你可以在完成必做与选做后,根据自身兴趣与时间投入进行深度拓展。

8.1 交互体验增强 ✨

现代 Shell 的一大特色是提供丰富的交互体验。你可以考虑实现命令提示符的自定义与主题化,让用户能够展示当前时间、用户名、主机名、路径等信息,甚至支持彩色显示和动态更新。

命令自动补全是另一个极大提升效率的功能,当用户按下 Tab 键时,Shell 可以根据当前目录下的文件名或可执行命令列表进行智能补全。

更进一步,你可以实现命令历史管理,将用户执行过的命令保存到如 ~/.tsh_history 这样的文件中,并支持类似 Ctrl+R 的历史搜索功能。

行编辑能力也是现代 Shell 的标配。你可以引入 GNU Readline 或 libedit 库,或者自己实现简单的行编辑功能,支持光标移动、删除、撤销等操作。

语法高亮则能让用户在输入时直观地区分命令、参数、字符串等不同元素,甚至可以实时提示语法错误,大大降低使用门槛。

8.2 脚本与语言特性 📝

Shell 不仅是执行命令的工具,也是一种编程语言。你可以为你的 Shell 添加内建函数或脚本模块化加载功能,让用户能够编写并调用复杂的功能模块。

命令别名(alias)是另一个实用功能,让用户可以为常用命令定义简写形式,如 alias ll='ls -l'

更进阶的特性包括扩展脚本语言能力,如变量定义、条件语句、循环结构等,甚至可以支持函数定义和局部变量作用域。

高级管道和进程间通信机制也是 Shell 脚本能力的重要组成部分,你可以探索如何支持多重管道、管道与子 Shell 的结合使用等。

8.3 系统集成扩展 🔌

真正实用的 Shell 需要与操作系统深度集成。PATH 管理与命令查找优化是一个很好的切入点,你可以支持在运行时动态添加、删除PATH中的条目,并将其持久化到配置文件。还可以使用缓存或哈希表对 PATH 下的可执行文件进行索引,加速命令查找。

配置文件与插件系统可以大大增强 Shell 的可定制性和扩展性。你可以设计读取 ~/.tshrc 这样的配置文件,在启动时加载用户的环境变量、自定义函数、别名等设置。更进一步,你可以定义插件接口,允许用户通过动态库扩展 Shell 的能力。

8.4 性能与安全 🛡️

对于实际使用的 Shell,性能和安全同样重要。优化 Shell 的启动速度和内存占用是一个值得探索的方向,可以考虑懒加载部分功能,避免在初始化时进行大量预加载。同时,良好的内存管理可以防止在执行复杂命令或脚本时出现内存泄漏。

在安全方面,你可以实现命令执行的权限控制和安全检查,如白名单/黑名单机制,防止执行某些高危操作。对命令执行路径和权限的严格检查则可以预防路径注入攻击。

8.5 参考资源 📚

  • POSIX Shell 标准 有助于理解 Shell 语法的最小通用规范和关键特性。
  • GNU Bash 参考手册 Bash 包含了大量进阶功能,如命令替换、算术表达式、扩展模式匹配等,能为自定义 Shell 提供借鉴。
  • Zsh 官方文档 Zsh 在交互性、可扩展性方面非常灵活,很多功能或设计理念值得学习。
  • 高级 Bash 脚本指南 其中对 Shell 脚本的高级语法、技巧、陷阱均有说明,可以作为实现高级功能时的参考。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •