minishell的实现步骤:
1、获取标准输入,解析字符串得到命令+运行参数
2、判断命令是否内建
3、创建子进程,让子进程“背锅”(进行程序替换)
4、父进程,进行进程等待
重定向:改变文件描述符所对应的描述信息(改变了数据流行,数据从原本要写入的文件流向了新的文件)。
重定向不能在父进程中完成的,父进程是需要打印数据的。
minishell中重定向的实现:
在原有字符串解析完毕的情况下,通过[ls-l >a.txt]解析之后获取得到
argv[]={"ls","-l",">","a.txt",NULL}
在子进程中判断argv中是否包含>或>>来决定是否需要重定向,
以及重定向方式(> 清空,>>追加);
fd=open(O_TRUNC/O_APPEND);
dup2(fd,1)将标准输出冲顶下到指定文件中
\n刷新缓冲区仅仅针对的标准输出文件,对其他磁盘文件并不具备刷新缓冲区效果
系统调用因为没有缓冲区,所以不会执行刷新缓冲区
程序运行从用户态切换到内核态运行(切换方法:发起系统调用/程序异常/程序中断)
区别:完成的功能是由用户自实现接口还是系统调用接口(当前功能自实现还是系统实现)
实现代码:
/* minishell 的实现
* 1、获取标准输入,解析字符串得到命令
* 2、判断命令是否重建
* 3、创建子进程,让子进程“背锅”
* 4、父进程进行进程等待
* */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
char buf[1024] = { 0 };
int do_face()
{
memset(buf,0,1024); // 将buf中的1024个字节初始化为0x00
printf("[user@localhost]$");
fflush(stdout); // 刷新标准输出的缓冲区
if(scanf("%[^\n]%c", buf) !=1)
{
getchar();
return -1;
}
return 0;
}
int argc=0;
char* argv[64];
int do_prase()
{
argc = 0;
char* ptr=buf;
while(ptr !='\0')
{
if(!isspace(*ptr))
{
argv[argc]=ptr;
argc++;
while(!isspace(*ptr) && *ptr!='\0')
{
ptr++;
}
*ptr='\0';
}
ptr++;
}
argv[argc]=NULL;
return 0;
}
int main()
{
while(1)
{
if(do_face() < 0)
{
continue;
}
if(do_prase()<0)
{
continue;
}
// 实现shell,功能
if(strcmp(argv[0],"cd")==0)
{
// 改变工作路径
chdir(argv[1]);
continue;
}
// 创建子进程
int pid =fork();
if(pid <0) // 创建失败
{
perror("fork error");
return -1;
}
else if(pid ==0) // 创建成功
{
// 在子进程中重定向
int i=0;
for(i =0 ;i < argc ;i++)
{
if(strcmp(argv[i], ">") == 0)
{
int fd = open(argv[i+1], O_WRONLY | O_CREAT | O_TRUNC );
dup2(fd ,1);
argv[i] = NULL;
}
else if(strcmp(argv[i] , ">>") == 0)
{
int fd = open(argv[i+1], O_WRONLY | O_CREAT | O_TRUNC );
dup2(fd ,1);
argv[i] = NULL;
}
}
execvp(argv[0] , argv);
exit(-1);
}
wait( NULL );
}
return 0;
}