这个小程序适合初学者在学习 Linux 进程时,对这部分知识的复习巩固。尤其是 exec
系和 wait
系 函数的应用。
就像题目说的那样,这是一个简易的 shell,以后有机会可能会出功能比较全面的 shell,敬请期待~
1. 功能演示
![946770eb56bde8e2d8e8ef18f7eedc18.png](https://i-blog.csdnimg.cn/blog_migrate/a219aafd963ba99f34d9e15f8f12d04b.png)
视频讲解:https://b23.tv/wK5Iys
2. 源码分享
1. 处理输入
我们在使用 shell 时,肯定会输入很多数据,我们先获取输入的数据,将其保存在一个缓冲区中。
int get_input(char buf[]){
int ret, ch;
memset(buf, 0x00, BUFFSIZE);
// %*c 用来回收正常输入命令后面的回车键
// 如果只输入回车键,因为 scanf 的第一个参数就不匹配,scanf 直接返回,回车键不会被处理
ret = scanf("%[^n]%*c", buf);
// 处理只输入回车的情况
// 添加 if 判断时处理输入 空格加回车的情况
if(ret == 0){
ch = getchar();
}
return ret;
}
2. 解析输入
一般我们在输入命令时,肯定不会规规矩矩的(=_=
所以,我们需要处理输入的数据,设置一个字符串指针数组 argv
,然后让它的元素指向我们解析好的一个个单词。这样做的好处是:第三步处理输入时,可以将 argv 直接作为 execvp
函数的第二个参数。
void parse(char* buf){
int i, flg = OUT;
int argc = 0;
char* argv[MAX_CMD];
for(i = 0; buf[i] != '0'; i++){
// 进入单词
if(flg == OUT && !isspace(buf[i])){
// 状态设置为 IN
flg = IN;
// argv 成员指向 buf 字符数组中单词的开始
argv[argc++] = buf + i;
}
// 离开单词
else if(flg == IN && isspace(buf[i])){
// 状态设置为 OUT
flg = OUT;
// 单词结束处设置终止字符
buf[i] = '0';
}
}
// 因为进程替换我们要使用 execvp 函数,argv 指针数组应该符合参数格式
argv[argc] = NULL;
do_cmd(argc, argv); // 执行命令
}
3. 处理输入
shell 命令分为四种:
- 可执行程序: 像
/usr/bin
中看到的所有文件一样。 - shell 内置命令: bash 支持的成为 shell builtin 的内置命令。例如,cd
- shell 函数: 合并到环境变量中的小型 shell 脚本。
- alias 命令: 在其他命令的基础上定义自己的命令
其中可执行程序的实现就是 fork 出子进程然后使用 exec 函数进行进程替换。
这里要注意的是,exec 只能替换第一种命令。所以 cd 命令无效,这样工作目录就没法更改了。
void do_cmd(int argc, char* argv[]){
pid_t pid;
switch(pid = fork()) {
// 如果 fork 出错,可以提示维护信息
case -1:
printf("当前 shell 维护中...");
return;
// 处理子进程
case 0:
execvp(argv[0], argv);
// 如果子进程被成功替换,就不可能执行下面的代码
printf("%s: command not foundn", argv[0]);
exit(1);
default: {
int status;
waitpid(pid, &status, 0); //等待子进程返回
// 处理子进程的返回码
}
break;
}
}
main 函数就相当简单了,这里就不写了。
源代码获取
源代码获取方式在原文文末,原文链接: https:// mp.weixin.qq.com/s/wUzH L4DYsf2_o_Zs59qB8A
如果你对这个代码有任何问题,欢迎和我交流!
C/C++/Java 方向学习资料分享,持续更新:
![6617917537f3690eb55288836a0ec3d0.png](https://i-blog.csdnimg.cn/blog_migrate/b455ea29a3e8c64f27ecf824872e2b50.jpeg)