minishell的实现
实现原理
我们在这里手动实现一个小型的shell,可以用来处理我们一般常规的指令如ls
,还可以附加参数-l
,并且还可以进行重定向操作。
实现原理很简单,我们将用户输入的字符串读取到我们的缓冲区中,然后首先遍历一遍整个缓冲区看是否存在重定向符>
或者>>
,并且将重定向符改为\0
并且读取出重定向的文件,随后保存判断结果。之后我们再次遍历一遍进行指令处理,我们利用字符指针数组让每一个指针指向每一个指令,并且将之间的空白符如(空格)
改为\0
。做完以上这一切,创建子进程利用进程替换将其替换为目标指令中的程序让其执行指定功能,如果有重定向符要先打开指定文件并且将标准输入重定向到指定文件中,者必须在子进程中完成,然后等待子进程关闭,父进程阻塞,并且让以上过程不断循环即可。
代码实现
/*minishell实现*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
char buf[1024] = {0};
char* argv[32];
int argc = 0;
//指令输入到缓冲区中
void do_face()
{
printf("[san@localhost]$ ");
fflush(stdout);
memset(buf, 0x00, 1024);
//%[^\n] 获取数据直到遇到\n
//%*c 取出一个字符丢弃
//利用正则表达式,取出指令输入字符串
if (scanf("%[^\n]%*c", buf) != 1)
{
//如果没有输入指令我们需要处理回车,将回车从缓冲区取出
getchar();
}
return ;
}
//处理指令,将字符指针数组每个指针指向每个指令
void do_parse()
{
char *ptr = buf;
argc = 0;
while(*ptr != '\0')
{
//当前位置非空白字符
if (!isspace(*ptr))
{
argv[argc++] = ptr;
while(!isspace(*ptr) && *ptr != '\0')
{
ptr++;
}
}
//所有空字符都换为'\0'
else
{
*ptr = '\0';
ptr++;
}
}
}
argv[argc] = NULL;
return;
}
int main()
{
// ls >> > a.txt
// int fd = open(a.txt);
// dup2(fd, 1);
// 将原先要写入到标准输出1中的数据,写入到指定文件中
while(1)
{
do_face();
//ls >> a.txt
//解析命令中是否有重定向指令
//没有则跳过此步骤
int redirect = 0;
char *file = NULL;
char *ptr = buf;
while(*ptr != '\0')
{
if (*ptr == '>')
{
redirect = 1;//清空重定向
*ptr++ = '\0';
if (*ptr == '>')
{
redirect = 2;//追加重定向
*ptr++ = '\0';
}
//isspace如果是空字符(制表符回车空格等则返回1)
//循环跳过中间所有空字符
while(isspace(*ptr) && *ptr != '\0')
{
ptr++;
}
file = ptr;
//拿到要写入的文件名存入file
while(!isspace(*ptr) && *ptr != '\0')
{
ptr++;
}
*ptr = '\0';
}
ptr++;
}
//解析流程:取出空白字符,获取程序名称和参数
do_parse();
//创建子进程
int pid = fork();
if (pid < 0)
{
exit(-1);
}
else if (pid == 0)
{
//重定向必须在子进程当中完成
//重定向处理
if (redirect == 1)
{
//清空重定向
int fd = open(file, O_CREAT|O_WRONLY|O_TRUNC, 0664);
//将标准输出重定向到file所指向的文件中
dup2(fd, 1);
}
else if (redirect == 2)
{
//追加重定向
int fd = open(file, O_CREAT|O_WRONLY|O_APPEND, 0664);
//将标准输出重定向到file所指向的文件中
dup2(fd, 1);
}
//将子进程替换为指令中要求的程序,执行相应的指令
execvp(argv[0], argv);
//防止子进程替换失败
exit(0);
}
wait(NULL);
}
return 0;
}