您的位置:首页 > 运维架构 > Shell

Shell实现(四) 执行命令的实现(包含管道的实现)

2015-10-24 23:03 525 查看

基本思路:

首先是执行命令的实现

通过exec族函数实现的

然后是管道的实现,有两种实现方案,一种是调用pipe函数,但是既然可以自己写一个,为什么要记这么一个傻逼的函数呢?

调用pipe函数很无脑,需要一个规模为2的文件描述符数组,用来表示向管道中写的流和从管道中读的流

自己设计一个也很无脑,建立一个文件作为管道的载体,然后方法和上面的类似。

具体实现:

第一种方案是代码比较简单的自己实现的pipe的版本

void run_shell (  )
{
int i,j;
fd[0] = open ( PIPE_FILE , O_CREAT|O_RDONLY , 0666 );//作为从管道中读出数据的端口
fd[1] = open ( PIPE_FILE , O_CREAT|O_WRONLY|O_TRUNC , 0666 );//作为向管道中写入数据的端口
for ( i = 0 ; i < cnt_group ; i++ )//遍历所有的命令组
{
l = group[i].first;
r = group[i].last;
int pid = fork();//创建一个子线程执行命令
//fork会有两个返回值,在父线程中返回值为非0,子线程返回值为0
if ( pid == 0 )
run_command ( l , r-1 ); //执行指令(我采用的递归的方法实现)
else
waitpid ( 0 , NULL , 0 );//父线程阻塞,等待子线程执行完毕(不提供后台执行的解决方案)
}
}


然后是执行命令,想到管道的设计,一下子想到了递归,虽然迭代也很好实现,但是因为是在是懒,还是选择递归地写…

void run_command ( int l , int x )
{
//printf ( "%d %d\n" , l , x );
pid_t pid;
if ( x != l )//如果不是最后一条指令,那么继续创建新的进程
{
pid = fork();
if ( pid==0 )//儿子进程跑前一条指令
run_command ( l , x-1 );
else waitpid ( 0 , NULL , 0 );//阻塞父亲进程等待儿子进程
}
//printf ( "where am i %d\n" , x );
//根据管道重定向输入输出
if ( x != l ) dup2 ( fd[0] , fileno(stdin) );
if ( x != r-1 ) dup2 ( fd[1] , fileno(stdout) );
    //执行命令
execvp ( cmd[x].cmd , cmd[x].param );
}


第二种方案是利用库中的pipe函数实现的方案

void run_shell ( )
{
int pipe_fd[2];
int status;
pipe(pipe_fd);
pid_t child1,child2;
if ((child1=fork())!=0)//父进程
{
if ( (child2 = fork()) == 0 )//子进程
{
close ( pipe_fd[1] );
close ( fileno ( stdin ) );
dup2 ( pipe_fd[0] , fileno(stdin));
close ( pipe_fd[0] );
execvp ( cmd[1].cmd , cmd[1].param );
}
else
{
close ( pipe_fd[0]);
close ( pipe_fd[1]);
waitpid ( child2 , &status , 0 );
}
waitpid ( child1 , &status , 0 );
}
else
{
printf ( "subshell 3cmd %d\n" , getpid() );
close ( pipe_fd[0] );
close ( fileno(stdout));
dup2 ( pipe_fd[1] , fileno(stdout));

close ( pipe_fd[1] );
execvp ( cmd[0].cmd , cmd[0].param );
}
}


贴心的小提示

需要用到的函数

1.

int execvp(const char *file ,char * const argv []);
//execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。


2.

pid_t waitpid(pid_t pid,int * status,int options);
//waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。


3.

#include<unistd.h>
int pipe(int fd[2])
//函数传入值 fd[2]:管道的两个文件描述符,之后就是可以直接操作这两个文件描述符
//返回值 成功 0  失败 -1


4.

int dup2(int oldhandle,int newhandle);
//复制文件句柄


5.

#include<fcntl.h>
int open(constchar*pathname,intflags);
int open(constchar*pathname,intflags,mode_tmode);
//返回值:成功则返回文件描述符,否则返回-1


6.

close( int )
//关闭文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  shell