综合项目:实现 MyShell
项目目标
实现一个简单的 Shell,综合运用 F 级所学知识。
功能要求
基础功能(必做)
- 命令执行
- 执行外部命令(如 ls, cat, echo)
- 支持命令参数
- 内置命令
cd:切换目录pwd:显示当前目录exit:退出 Shell
- 输入输出重定向
>:输出重定向<:输入重定向
- 管道
|:连接多个命令
进阶功能(选做)
- 后台执行
&:后台运行命令
- 环境变量
- 支持
$VAR变量替换
- 支持
- 命令历史
- 上下键查看历史命令
实现框架
// 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;
}
实现步骤
第一阶段:基础框架
- 实现命令行读取和解析
- 实现外部命令执行
- 实现内置命令(cd, pwd, exit)
第二阶段:重定向
- 实现输出重定向
> - 实现输入重定向
<
提示:使用 dup2() 系统调用
// 输出重定向示例
int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(fd, STDOUT_FILENO);
close(fd);
第三阶段:管道
- 实现管道
|
提示:使用 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
提交要求
- 完整的源代码
- Makefile 或 CMakeLists.txt
- README.md 说明文档
- 测试截图
- GitHub 仓库链接
评分标准
- 基础功能(60%)
- 进阶功能(20%)
- 代码质量(10%)
- 文档完整(10%)
参考资源
完成后进入 E 级课程!