四时宝库

程序员的知识宝库

c/c++Linux 模拟实现shell-----输出重定向

模拟实现shell

思路:

读取命令行的字符串,将一整句字符串以空格为分隔符分成若干个子串,再将每个子串的地址保存在一个数组argv中

使用fork函数创建一个子进程(fork函数),在子进程中将数组argv中存储的字符串指针传参给进程控制函数execvp,执行相应文件名的文件(进程);

父进程等待子进程退出,并且回收子进程,防止子进程和父进程并发执行

execvp函数

函数原型:

int execvp(const char file, char const argv []);

1

函数说明:

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

返回值:

如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。

execvp()函数实现过程:

删除mmu表

内存开辟一块空间

把可执行程序的代码复制到这块空间

改变PCB中部分值,其中包括下一条要执行的指令的地址

重新建mmu表

注意:

execvp()会用即将运行的进程的内存替换为要调用的进程的内存,更进一步讲,就是把当前进程的机器指令都清空,然后载入被execvp运行起来的进程的机器指令。

代码:

#include<stdio.h>

#include<unistd.h>

#include<stdlib.h>

#include<string.h>

void do_prase(char* buf)

{

char* argv[8];

int status=0;

int argc=0;

int i=0;

for(;buf[i]!=0;++i)

{

if(status==0 && !isspace(buf[i]))//以空格为界,分割命令行的子串

{

status=1;

argv[argc++]=buf+i;

}

else if(isspace(buf[i]))

{

status=0;

buf[i]==0;

}

}

argv[argc]=NULL;

do_execute(argc,argv);

}

void do_execute(int argc,char* argv[])

{

pid_t pid=fork();//创建子进程

if(pid==-1)

{

perror("fork");

exit(EXIT_FAILURE);

}

else if(pid==0)

{

execvp(argv[0],argv);//子进程进行替换,将子进程替换成要执行的内存,交换成功不返回,失败返回-1

perror("execvp");

exit(EXIT_FAILURE);

}

else

{

int st;

while(wait(&st)!=pid)//父进程阻塞等待子进程,并且回收子进程

;

}

}

int main()

{

char buf[1024];

while(1)

{

memset(buf,0,sizeof(buf));

printf("myshell>> ");

while(scanf("%[^\n]%*c",buf)==0)//读取命令行

{

while(getchar()!='\n');

}

//exit为内置命令,不可进行交换

if(strncmp(buf,"exit",4)==0)

exit(0);

do_prase(buf);

}

return 0;

}

效果:

输出重定向功能

思路:

从上面简单的shell实现过程中,在argv数组中读取的字符串中有 ” > “时,表示要进行输出重定向

将 “>” 后面的文件(file)打开,表示要将你的内容输入到这个文件中去

关闭文件描述符1(标准输出),将打开的文件描述符复制过来

此时输入内容时,找到文件描述符最小的就是你所打开的文件

进行进程替换

原理图:

代码:

void do_execvp(int argc,char* argv[])

{

if(fork() == 0)//子进程会进入if

{

//输出重定向

int i = 0;

for(i = 0; i < argc ;++i)

{

if(strcmp(argv[i], ">") == 0)

{

char* file = argv[i+1];

argv[i] = NULL;

int fd = open(file,O_WRONLY | O_RDONLY,0666);//打开文件

close(1);//关闭文件描述符1

dup(fd);//将打开的文件描述符复制过来

break;

}

}

execvp(argv[0],argv);//execvp会将正在运行的进程的内存替换成要执行的进程的内存

perror("execvp");//如果走到这步,说明execvp执行失败,因为如果执行成功,这句代码所在的内存已经被替换了,这句代码早就不在了

exit(0);//则perror报错并退出程序

}

wait(NULL);//加一个阻塞,等待子进程结束父进程再运行,防止父进程和子进程并发运行

}

void do_parse(char* buf)

{

char* argv[8]={};

int argc=0;

int i=0;

int status=0;

for(i=0;buf[i]!=0;i++)

{

if(status == 0 && !isspace(buf[i]))

{

argv[argc++]=buf+i;

status=1;

}

else if(isspace(buf[i]))

{

status=0;

buf[i]=0;

}

}

argv[argc]=NULL;

do_execvp(argc,argv);

}

int main()

{

char buf[1024];

while(1)

{

printf("my shell>>");

memset(buf,0,sizeof(buf));

while(scanf("%[^\n]%*c",buf) == 0)

{

while(getchar()!='\n');

printf("my shell>>");

}

//内置命令:不可以使用execvp执行

//eg:exit

if(strncmp(buf,"exit",4) == 0 )

{

exit(0);

}

do_parse(buf);

}

}

效果展示:

更多linux免费视频资料获取 后台私信【架构】

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接