shell就是一个命令解释器,它互动式地解释和执行用户输入的命令;当有命令要执行时,shell创建子进程让子进程去执行命令,而shell只需要等待子进程执行完退出即可。
具体步骤:
- 获取终端输入的命令
- 解析命令
- 创建子进程
- 对子进程进行程序替换
- 等待子进程执行完后退出
#include <stdio.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LEN 1024 // 设置命令最大长度
#define NUM 32 // 命令拆分后的最大个数
int main()
{
char cmd[LEN]; // 存储命令
char* myargv[NUM]; // 存储命令拆分后的结果
char hostname[32]; // 主机名
char pwd[128]; // 当前目录
while (1) {
//获取命令提示信息
struct passwd* pass = getpwuid(getuid());
gethostname(hostname, sizeof(hostname) - 1);
getcwd(pwd, sizeof(pwd) - 1);
int len = strlen(pwd);
char* p = pwd + len - 1;
while (*p != '/') {
p--;
}
p++;
// 打印命令提示信息
printf("[%s@%s %s]$ ", pass->pw_name, hostname, p);
// 读取命令
fgets(cmd, LEN, stdin);
cmd[strlen(cmd) - 1] = '\0';
// 拆分命令
myargv[0] = strtok(cmd, " ");
int i = 1;
while (myargv[i] = strtok(NULL, " ")) {
i++;
}
pid_t child = fork(); // 创建子进程执行命令
if (child == 0) {
//child
execvp(myargv[0], myargv); // 子进程进行程序替换
exit(1); // 替换失败的退出码设置为1
}
// 父进程 / myshell
int status = 0;
pid_t myshell = waitpid(child, &status, 0); // shell等待子进程退出
if (myshell > 0) {
printf("exit code:%d\n", WEXITSTATUS(status)); // 打印子进程的退出码
}
}
return 0;
}
补充:
- 当自己的命令解释器(myshell)运行起来后,每次子进程执行完任务退出后都会打印退出码,可以以此来分辨自己写的和操作系统的命令解释器。
- 我们自己手写的shell总体上是有一些缺陷的,在读取终端输入的时候会直接读取,方向键和删除键也是会被读进去的(因为方向键在终端中通常被表示为一系列的字节序列),如果想让其发挥功能就得要做对其进行特殊处理。