C语言实现一个Linux环境下的shell
一.实现一个shell的流程
1.捕捉键盘输入
将输入的命令和参数存储到一个数组中
2.解析命令
将输入的命令和参数分解成一个个独立的部分
3.创建子进程
因为shell是一个可以一直运行的程序,所以不能运行之后就退出,如果创建一个子进程,在子进程执行操作,子进程执行完毕后退出也不会对父进程造成影响,所以程序可以一直运行
4.在子进程中进行进程替换
将要完成的任务交给子进程
5.父进程等待子进程退出
防止产生僵尸进程
二.源码
#include <stdio.h>
#include <string.h>
#include <unistd.h> //fork
#include <sys/types.h>
#include <sys/stat.h> //umask
#include <sys/wait.h> //wait
#include <fcntl.h>
#define MAX_ARGU_SZ 1024 //最大参数大小
char arguments[MAX_ARGU_SZ]; //参数数组
int main(int argc, char* argv[])
{
while(1)
{
//将参数数组全部清空
memset(arguments, 0, MAX_ARGU_SZ);
//1.获取键盘输入
printf("[user:host /]:$ ");
//手动刷新文件缓冲区
fflush(stdout);
//获取一行输入
fgets(arguments, MAX_ARGU_SZ - 1, stdin);
int len = strlen(arguments);
//过滤直接回车的命令
if(*arguments == '\n')
{
continue;
}
//处理退出命令
if(*arguments == 'Q' || *arguments == 'q')
{
break;
}
//将最后输入的\n替换为\0
arguments[len - 1] = '\0';
//1.1:检测重定向
char* str = arguments;
int numOfRe = 0; //重定向符号>>的个数 0:不重定向 1:覆盖 2:追加
char* RedirFile = NULL; //重定向的文件名
while(*str != '\0')
{
if(*str != ' ')
{
if(*str == '>')
{
numOfRe++;
*str = '\0';
str++;
if(*str == '>')
{
numOfRe++;
*str = '\0';
str++;
}
//指针跳转到重定向文件名的位置
while(*str != '\0' && *str == ' ')
{
str++;
}
RedirFile = str;
//指针跳转到重定向文件名末尾的下一个位置
while(*str != '\0' && *str != ' ')
{
str++;
}
*str = '\0';
}
}
str++;
}
//2.解析用户命令
char* command[32] = {NULL};
str = arguments;
int commandIdx = 0;
while(*str != '\0')
{
if(*str != ' ')
{
command[commandIdx++] = str;
//走到空格位置或者结束位置
while(*str != '\0' && *str != ' ')
{
str++;
}
*str = '\0'; //断开参数之间的联系
}
str++;
}
command[commandIdx] = NULL; //参数数组最后一位设置为NULL,在进程替换时会用到
//判断操作是否为cd,如果是就改变操作路径,结束本轮操作
if(strcmp("cd", command[0]) == 0)
{
chdir(command[1]);
continue;
}
//3.创建子进程
pid_t pid = fork();
if(pid < 0)
{
//创建子进程失败,继续创建直到成功为止
perror("fork error");
continue;
}
else if(pid == 0)
{
int fd = -1;
//进程替换之前检查重定向
//覆盖重定向
if(numOfRe == 1)
{
umask(0);
fd = open(RedirFile, O_RDWR | O_CREAT | O_TRUNC, 0664);
dup2(fd, 1);
}
//追加重定向
else if(numOfRe == 2)
{
umask(0);
fd = open(RedirFile, O_RDWR | O_CREAT | O_APPEND, 0664);
dup2(fd, 1);
}
//4.替换子进程
//int execvp(const char *file, char *const argv[]);
int ret = execvp(command[0], command);
//创建子进程失败退出
if(ret == -1)
{
break;
}
if(fd != -1)
{
close(fd);
}
}
//5.父进程等待子进程退出
wait(NULL);
}
return 0;
}
三.遇到的问题
1.不知道如何获取输入
解决办法:对于输入有空格的问题,用scanf难以解决,所以需要使用fgets
2.解析命令时不知道如何存储用什么数据结构来存储命令
解决办法:采用指针数组
3.对于cd命令没有特殊处理
解决办法:cd命令需要更改当前的工作目录;
4.无法实现文件输出重定向
通过对命令的解析,在子进程中使用dup2对标准输出进行重定向