用c语言的实现一个简单的交互式shell

处理思想:

1.读取命令行参数:fgets(buff,50,stdin)

2.以管道符号|将命令行字符串分解成若干个子字符串,推荐使用函数strtok_r()

3.以空格符号‘  ’将第二步分解得到的子字符串继续分解,得到每一个参数,推荐使用函数strtok()

4.strtok与strtok_r函数的区别,见我的博客点击打开链接

代码如下:

/*************************************************************************
    > File Name: my_shell.c
    > Author: lipan
    > Mail: ma6174@163.com 
    > Created Time: Sun 27 Jul 2014 10:20:56 PM EDT
 ************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
struct command
{
char *arg[15]; /*用于存放命令行参数的指针数组*/
char *in; /*存放输入重定向的文件名,其实这个变量也可以直接存放到arg中的,将他们分开存放方便读取数值*/
char *out; /*存放输出重定向的文件名*/
};
typedef struct command cmd;
int main(int argc,char *argv[])
{
char buff[50];
char pathname[50];
cmd cmds[10]; /*结构数组,它的每一个元素都是一个结构体*/
pid_t pid;
int fd_in; /*输入重定向文件描述符*/
int fd_out; /*输出重定向文件描述符*/
int i=0;
int pipe_num=0;
int j=0;
int fd[10][2]; /*开辟10个管道描述符*/
int cmd_num=0; /*以管道符号分开的命令数目*/


while(1)
{
/*获取初始的工作路径*/
memset(pathname,50,0);
getcwd(pathname,50); /*获取当前工作路经*/
printf("[my@localhost %s]",pathname);
fflush(stdout); /*刷新缓冲区*/



memset(buff,50,0);
fgets(buff,50,stdin); /*获取命令行所有参数*/
buff[strlen(buff)-1]='\0'; /*这点处理非常有必要,直接输入内容给一个buffer赋值时,最好给buffer后面添加一个'\0'*/
/*cmds存放的是以管道符|分开的命令行字符串,该字符串中又包含若干个命令参数,而arg中存放的是单个的命令行参数*/


cmd_num=parse_pipe(buff,cmds); /*cmd_num就是以管道符号分开的命令字符串的个数*/


cd_command(cmds[0].arg[0],cmds[0].arg[1]);  /*调用cd命令函数*/

/*便于分析结构数组的值而打印输出*/
/*printf("cmds[0]=        %s\n",cmds[0]);
printf("cmds[0].arg[0]=          %s\n",cmds[0].arg[0]);
printf("cmds[0].arg[1]=          %s\n",cmds[0].arg[1]);
printf("cmds[0].in=       %s\n",cmds[0].in);
printf("cmds[0].out=          %s\n",cmds[0].out);


printf("cmds[1]=        %s\n",cmds[1]);
printf("cmds[1].arg[0]=          %s\n",cmds[1].arg[0]);
printf("cmds[1].arg[1]=          %s\n",cmds[1].arg[1]);
printf("cmds[1].in=       %s\n",cmds[1].in);
printf("cmds[1].out=          %s\n",cmds[1].out);
fflush(stdout);*/


pipe_num=cmd_num- 1;


if(pipe_num>10) /*因为预先最多分配了10个管道描述符,所以如果从终端获取的管道个数大于10,就重新输入命令行*/
continue;
for(i=0;i<pipe_num;i++)
pipe(fd[i]);


for(i=0;i<cmd_num;i++) /*i等于以管道符分开的命令个数*/
{
if((pid=fork())==0) /*创建进程,如果子进程先执行,跳出循环,执行子进程代码段,如果父进程先执行,接着创建,最后的结果就是产生一个父进程,cmd_num个子进程*/
break;
if(pid<0)
perror("fork");
}
if(pid==0)   /*这里有多少个子进程就执行多少次*/
{
/*重定向输入*/
if(cmds[i].in)  /*执行第一个子进程时i=0,执行第二个子进程时i=1,依次递增*/
{
fd_in=open(cmds[i].in,O_RDONLY);
if(fd_in<0)
perror("open");
dup2(fd_in,STDIN_FILENO);
close(fd_in);
}
/*重定向输出*/
if(cmds[i].out)
{
fd_out=open(cmds[i].out,O_RDWR|O_CREAT|O_TRUNC,0644);
if(fd_out<0)
perror("open_out");
dup2(fd_out,STDOUT_FILENO);
close(fd_out);
}
/*管道: 它是进程间的一种通信方式,这里采取的是多个子进程间通信*/
if(pipe_num) /*如果管道个数为0则不执行,否则执行*/
{
if(i==0)
{
close(fd[i][0]); /*i=0肯定是第一个子进程,关闭第一个子进程的管道的读端*/
dup2(fd[i][1],1); /*将标准输出重定向到第一个子进程管道的写端,本来第一个命令字符串的执行结果输出到屏幕的,现在输出到管道的读端*/
close(fd[i][1]); /*关闭第一个子进程管道的写端*/
for(j=1;j<pipe_num;++j) /*关闭多余的管道,当然这里只创建了一个管道*/
close(fd[j][0]),close(fd[j][1]);
}
else if(i==pipe_num) /*假设i=1肯定是第二个子进程,并且只有一个管道,那么条件成立*/
{
close(fd[i-1][1]); /*关闭管道的写端*/
dup2(fd[i-1][0],0); /*将标准输入重定向到第二个子进程的读端,本来第二个命令字符串的输入是直接由键盘输入的,现在直接从管道的读端获取,从而达到了两个进程的通信*/
close(fd[i-1][0]); /*关闭第二个子进车管道的读端*/
for(j=0;j<pipe_num-1;++j)
close(fd[j][0]),close(fd[j][1]);
}
else
{
dup2(fd[i - 1][0], 0);
close(fd[i][0]);
dup2(fd[i][1], 1);
close(fd[i][1]);
for (j = 0; j < pipe_num; ++j)
{
if((j!=i-1)||(j!=i))
close(fd[j][0]),close(fd[j][1]);
}
}
}
/*execlp(cmds[0].arg[0],cmds[0].arg[0],cmds[0].arg[1],cmds[0].arg[2],cmds[0].arg[3],NULL);*/
/*结构数组中指针数组元素的第0个参数一定是命令(这是由命令行输入决定的),第二个参数是命令参数*/
execvp(cmds[i].arg[0],cmds[i].arg);
}
if(pid>0)
{
for(i=0;i<pipe_num;++i)
close(fd[i][0]),close(fd[i][1]);
for(i=0;i<cmd_num;i++)
wait(NULL);
}
}
return 0;
}


/*以空格符分开命令行字符串*/
int parse_command_line(char *buf,cmd *cmd_buf) /*cmd_buf是一个结构体指针*/
{
int i=0;
cmd_buf->in=NULL;
cmd_buf->out=NULL;
char *p=strtok(buf," ");
while(p)
{
if(*p=='>')
{
if(*(p+1))      /*如果>后面有空格,那么执行完strtok后,空格被替换成'\0',*(p+1)就是'\0',为假,不执行cmd_buf->out=p+1*/
cmd_buf->out=p+1;
else
cmd_buf->out=strtok(NULL," "); /*如果>后面没有空格,那么执行完strtok后,>符号被替换成'\0'了,直接调用strtok函数*/
}
else if(*p=='<')
{
if(*(p+1))
cmd_buf->in=p+1;
else
cmd_buf->in=strtok(NULL," ");
}
else
cmd_buf->arg[i++]=p; /*如果获取的命令行参数不是>或者<,那么就将它们保存在arg中*/
p=strtok(NULL," "); /*当提取完成时,p=NULL,跳出while循环,把命令行的所有参数分开存放到arg中了*/
}
cmd_buf->arg[i]=NULL; /*把没有赋值的指针数组元素赋值为NULL*/
return 0;
}


/*以管道符分开命令行字符串*/
int parse_pipe(char *buf,cmd cmd_s[]) /*cmd是结构数组,它的每一个元素都是一个结构体*/
{
int n=0;
char *p;
/*这里使用strtok函数是不行的,必须使用strtok_r函数,它们的区别见博客*/
char *pt=strtok_r(buf,"|",&p);
while(pt)
{
parse_command_line(pt,&cmd_s[n++]);   /*以管道符分开的第一个字符串存放在结构数组cmd_s[0]中,第二个字符串存放在结构数组cmd_s[1]中,依次递推*/
pt=strtok_r(NULL,"|",&p);
}
return n;
}


/*cd内部命令*/
int cd_command(char *cd_command,char *path)
{
int return_value=0;


if(strncmp(cd_command,"cd",2)==0)
if((return_value=chdir(path))<0)
perror("chdir");
return return_value;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值