前面我們寫了一個基礎版本的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;
}
測試結果如下: