项目目标

实现一个简单的 Shell,综合运用 F 级所学知识。

功能要求

基础功能(必做)

  1. 命令执行
    • 执行外部命令(如 ls, cat, echo)
    • 支持命令参数
  2. 内置命令
    • cd:切换目录
    • pwd:显示当前目录
    • exit:退出 Shell
  3. 输入输出重定向
    • >:输出重定向
    • <:输入重定向
  4. 管道
    • |:连接多个命令

进阶功能(选做)

  1. 后台执行
    • &:后台运行命令
  2. 环境变量
    • 支持 $VAR 变量替换
  3. 命令历史
    • 上下键查看历史命令

实现框架

// myshell.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

#define MAX_LINE 1024
#define MAX_ARGS 64

// 解析命令行
void parse_command(char *line, char **args) {
    int i = 0;
    args[i] = strtok(line, " \t\n");
    while (args[i] != NULL && i < MAX_ARGS - 1) {
        i++;
        args[i] = strtok(NULL, " \t\n");
    }
    args[i] = NULL;
}

// 执行命令
void execute_command(char **args) {
    pid_t pid = fork();

    if (pid == 0) {
        // 子进程
        if (execvp(args[0], args) == -1) {
            perror("myshell");
        }
        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        // 父进程
        wait(NULL);
    } else {
        perror("fork");
    }
}

// 内置命令
int builtin_cd(char **args) {
    if (args[1] == NULL) {
        fprintf(stderr, "myshell: expected argument to \"cd\"\n");
    } else {
        if (chdir(args[1]) != 0) {
            perror("myshell");
        }
    }
    return 1;
}

int builtin_pwd(char **args) {
    char cwd[MAX_LINE];
    if (getcwd(cwd, sizeof(cwd)) != NULL) {
        printf("%s\n", cwd);
    } else {
        perror("myshell");
    }
    return 1;
}

int builtin_exit(char **args) {
    return 0;
}

// 主循环
int main() {
    char line[MAX_LINE];
    char *args[MAX_ARGS];

    while (1) {
        printf("myshell> ");
        fflush(stdout);

        if (fgets(line, MAX_LINE, stdin) == NULL) {
            break;
        }

        parse_command(line, args);

        if (args[0] == NULL) {
            continue;
        }

        // 检查内置命令
        if (strcmp(args[0], "cd") == 0) {
            builtin_cd(args);
        } else if (strcmp(args[0], "pwd") == 0) {
            builtin_pwd(args);
        } else if (strcmp(args[0], "exit") == 0) {
            break;
        } else {
            execute_command(args);
        }
    }

    return 0;
}

实现步骤

第一阶段:基础框架

  1. 实现命令行读取和解析
  2. 实现外部命令执行
  3. 实现内置命令(cd, pwd, exit)

第二阶段:重定向

  1. 实现输出重定向 >
  2. 实现输入重定向 <

提示:使用 dup2() 系统调用

// 输出重定向示例
int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);

第三阶段:管道

  1. 实现管道 |

提示:使用 pipe() 系统调用

int pipefd[2];
pipe(pipefd);

if (fork() == 0) {
    // 第一个命令
    dup2(pipefd[1], STDOUT_FILENO);
    close(pipefd[0]);
    close(pipefd[1]);
    execvp(cmd1[0], cmd1);
}

if (fork() == 0) {
    // 第二个命令
    dup2(pipefd[0], STDIN_FILENO);
    close(pipefd[0]);
    close(pipefd[1]);
    execvp(cmd2[0], cmd2);
}

close(pipefd[0]);
close(pipefd[1]);
wait(NULL);
wait(NULL);

测试用例

# 基础命令
myshell> ls
myshell> pwd
myshell> cd /tmp
myshell> pwd

# 重定向
myshell> echo "hello" > test.txt
myshell> cat < test.txt

# 管道
myshell> ls | grep ".c"
myshell> cat file.txt | wc -l

提交要求

  1. 完整的源代码
  2. Makefile 或 CMakeLists.txt
  3. README.md 说明文档
  4. 测试截图
  5. GitHub 仓库链接

评分标准

  • 基础功能(60%)
  • 进阶功能(20%)
  • 代码质量(10%)
  • 文档完整(10%)

参考资源

完成后进入 E 级课程

更新时间: