#include<unistd.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void do_parse(char *buf,char* argv[])
{
int i;
int argc=0;
int status=0;
for(i=0;buf[i];i++)
{
if( !isspace(buf[i]) && status==0)
{
argv[argc++]=buf+i;
status=1;
}else if( isspace(buf[i]) )
{
status=0;
buf[i]='\0';
}
}
argv[argc]=NULL;
}
void process_create(pid_t*pid,int func(const char*file,char*const argv[]),char*const argv[])
{
int pid1=*pid;
switch(pid1)
{
case -1:
perror("fork");
exit(EXIT_FAILURE);
break;
case 0:
//child
func(argv[0],argv);
perror("process_create");
exit(EXIT_FAILURE);
break;
default:
{//fater
int c;
while(wait(&c)!=pid1)
{
;
}
break;
}
}
}
int main()
{
char* argv[8];
int argc = 0;
char buf[1024]={0};
while(1)
{
printf("[myshell~]");
scanf("%[^\n]%*c",buf);
do_parse(buf,argv);
pid_t pid=fork();
process_create(&pid,execvp,argv);
memset(buf,0,1024);
}
}
以上为myshell 的代码部分,系统调用的执行函数为execvp,可以正常运行ls、vim、gcc 等命令,
但不能运行 | 、 cd 、 >等命令。
对main函数中的 scanf (" %[^\n] %*c" , buf) 进行解释:
%[ ]的意思为读取一个字符集
%[^\n]的意思为 读取 \n前的所有字符
%* *代表丢弃读取到的数据
%*c 表示丢到读取到底字符 (因为前面已经读取了\n前的数据,就剩下\n了,此句子中表示读取\n并丢弃它)。
总和的意思是将\n前的所有字符读取到buf中,将\n从缓冲区读取并丢弃。
关于system和popen与fork的区别:
fork 用来创建一个子进程 一个程序一调用fork函数,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。而如果两个进程要共享什么数据的话,就要使用另一套函数(shmget,shmat,shmdt等)来操作。现在,已经是两个进程了,对于父进程,fork函数返回了子程序的进程号,而对于子程序,fork函数则返回零,这样,对于程序,只要判断fork函数的返回值,就知道自己是处于父进程还是子进程中。
system 可以看做是fork + execl + waitpid。system()函数,功能强大 当system接受的命令为NULL时直接返回,否则fork出一个子进程,因为fork在两个进程:父进程和子进程中都返回,这里要检查返回的pid,fork在子进程中返回0,在父进程中返回子进程的pid,父进程使用waitpid等待子进程结束,子进程则是调用execl来启动一个程序代替自己,execl(“/bin/sh”, “sh”, “-c”, cmdstring, (char*)0)是调用shell,这个shell的路径是/bin/sh,后面的字符串都是参数,然后子进程就变成了一个shell进程,这个shell的参数是cmdstring,就是system接受的参数。在windows中的shell是command,想必大家很熟悉shell接受命令之后做的事了。
popen()也常常被用来执行一个程序
popen() 函数用创建管道的方式启动一个 进程, 并调用 shell. 因为管道是被定义成单向的, 所以 type 参数只能定义成只读或者只写, 不能是两者同时, 结果流也相应的是只读或者只写. command 参数是一个字符串指针, 指向的是一个以 null 结束符结尾的字符串, 这个字符串包含一个 shell 命令. 这个命令被送到 /bin/sh 以 -c 参数执行, 即由 shell 来执行. type 参数也是一个指向以 null 结束符结尾的字符串的指针, 这个字符串必须是 ‘r‘ 或者 ‘w’ 来指明是读还是写.popen() 函数的返回值是一个普通的标准I/O流, 它只能用 pclose() 函数来关闭, 而不是 fclose() 函数. 向这个流的写入被转化为对 command 命令的标准输入; 而 command 命令的标准输出则是和调用 popen(), 函数的进程相同,除非这个被command命令自己改变. 相反的, 读取一个 “被popen了的” 流, 就相当于读取 command 命令的标准输出, 而 command 的标准输入则是和调用 popen, 函数的进程相同.