前面我们写了一个基础版本的shell,但有些功能没有实现。这次我们直接在上次的基础上将代码稍加修改,使其支持输入输出重定向。
过程还是一样:
· 获取命令行
· 解析命令行
· 创建子进程(fork)
· 替换子进程(execvp)
· 父进程等待子进程退出(wait)
只不过这次解析命令行时需要对输入重定向 (<)和 输出重定向 (>)做特殊处理。
输入重定向:关闭标准输入,打开某普通文件,使其文件描述符为0.
输出重定向:关闭标准输出,打开某普通文件,使其文件描述符为1.
需要借助dup()函数来复制文件描述符,传给它一个旧的文件描述符,调用成功返回新的文件描述符,返回的新的文件描述符 newfd 和原来的文件描述符 oldfd 对应同一个文件表项(struct file结构体),所以共享同一个当前文件偏移量。
代码如下:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<ctype.h>
#include<string.h>
char *my_arg[32];
//执行命令
void do_execute()
{
pid_t id = fork();//创建子进程
if(id < 0)
{
perror("fork");
return;
}
else if(id == 0)//子进程做其他事
{
int i = 0;
int flag = 0;
//判断命令是否含输入输出重定向
for( ;my_arg[i] != NULL;i++)
{
//输出重定向
if(strcmp(my_arg[i],">") == 0)
{
flag = 1;
break;
}
//输入重定向
if(strcmp(my_arg[i],"<") == 0)
{
flag = 2;
break;
}
}
//处理输出重定向
if(flag == 1)
{
my_arg[i] = NULL;
int oldfd = open(my_arg[i+1],O_WRONLY|O_CREAT,0777);
if(oldfd < 0)
{
perror("open");
exit(1);
}
close(1);
int newfd = dup(oldfd);
if(newfd < 0)
{
perror("dup");
exit(1);
}
}
//处理输入重定向
if(flag == 2)
{
my_arg[i] = NULL;
int oldfd = open(my_arg[i+1],O_RDONLY,0644);
if(oldfd < 0)
{
perror("open");
exit(1);
}
close(0);
int newfd = dup(oldfd);
if(newfd < 0)
{
perror("dup");
exit(1);
}
}
execvp(my_arg[0],my_arg);
}
else
{ //父进程等子进程退出
int status = 0;
pid_t ret = waitpid(id,&status,0);
if(ret>0)
{
printf("sig: %d,exit code: %d\n",status&0x7F,(status>>8)&0xFF);
}
else
{
printf("waitpid running error\n");
}
}
}
//解析命令行
void do_parse(char* cmd)
{
cmd[strlen(cmd)-1] = 0;
char *p = cmd;
int i = 1;
my_arg[0] = cmd;
while(*p)
{
if(isspace(*p))
{
*p = 0;
p++;
my_arg[i++] = p;
}
else
{
p++;
}
}
my_arg[i] = NULL;
}
int main()
{
char cmd[128];
while(1)
{
printf("[myshell#] ");
fgets(cmd,sizeof(cmd),stdin);
do_parse(cmd);
do_execute();
}
return 0;
}
测试结果如下: