LinuxC学习之myshell的实现

    通过两天时间的努力,自己做的myshell终于成功了,现在分享一下自己做的时候出现的问题以及需要的地方。刚开始做的时候,自己框架不知道怎么搭,而且有很多细节想不通,就迟迟没有开写。整整过了一个早上之后,还是想不通,没办法了,就自己开始写吧,不能浪费时间不是,就一个一个函数写吧。这个决定让我自己最后有了一次大改的动作。
    在这里先介绍一下我的思路:
  1. 先将命令保存起来。
  2. 将命令解析出来,放到二维数组当中。
  3. 根据命令的不同,比如(cd,>),给出一个标记的值。
  4. vfork一个进程,因为这样,调用系统命令时候才能不将父进程关闭。
  5. 根据不同的标记值,进入不同的函数。
  6. 循环。


下面贴出我的代码:

#include <unistd.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>

void find_command(char ** argv1)
{
DIR * dir;
struct dirent * ptr;
pid_t pid;

// pid = vfork();

// if( pid == 0 ) {

if((dir = opendir("/usr/bin")) == NULL ) {
perror("opendir!\n");
exit(0);
}


while((ptr = readdir(dir)) != NULL ) {
if(strcmp(ptr->d_name,argv1[0]) == 0 ) {
execvp(argv1[0], argv1);
exit(0);
// if(execvp(argv1[0],argv1) < 0)
// perror("error!\n");
// printf("\nwhye\n");
// exit(0);
}
}
printf("\nok\n");
}

int chose(int count,char ** argv1)
{
int flag = 5,i;

if( strcmp( argv1[0],"cd") == 0 ) {
flag = 1;
}
for( i = 0 ; i < count ; i++ ) {
if( strcmp(argv1[i],">") == 0 )
flag = 2;
if( strcmp(argv1[i],"|") == 0 )
flag = 3;
}

return flag;
}

void change_dir(char ** argv1)
{
char buf[100];
if(chdir(argv1[1]) < 0) {
perror("chdir!\n");
}

getcwd(buf,100);
printf("%s\n",buf);

}

void outputdir(int count,char ** argv1)
{
int i,j,num;
int fd;
char * argv[10];
pid_t pid;

/* for( i = 0 , j = 0 ; i < count ; i++ ) {
if()
}
*/	

for( i = 0 , j = 0; i < count ; i++) {
if(strcmp(argv1[i],">") == 0 ) {
num = i+1;
break;
}
argv[j] = argv1[i];
j++;
}
argv[j] = NULL;
if((fd = open(argv1[num],O_EXCL | O_TRUNC | O_CREAT | O_RDWR , 0666)) < 0 ) {
perror("error!\n");
}

// pid = vfork();
// if( pid == 0 ) {
dup2(fd , 1);
if(execvp(argv1[0],argv) == -1) {
exit(-1);
}
exit(0);
// }
}

int main()
{
char argv[15][100],shell_order[100],*argv1[15];
int i,j,k,count = 0,flag,fd;
DIR * dir;
struct dirent * ptr;
pid_t pid;

while(1) {
count = 0;
printf("llh my_shell@:");
gets(shell_order);
for( i = 0 , j = 0 , k = 0 ; shell_order[k] != '\0' ; k++ , j++ ) {
if( shell_order[k] == ' ' ) {
argv[i][j] = '\0';
i++;
j = -1;
count++;
continue;
}
argv[i][j] = shell_order[k];

}
argv[i][j] = '\0';
count++;

// printf("%d\n\n",count);
for( i = 0 ; i < count ; i++ ) {
// puts(argv[i]);
argv1[i] = argv[i];
}
argv1[i] = NULL;

printf("%d\n",count);

flag = chose(count,argv1);
printf("%d\t\n\n\n",flag);

pid = vfork();
if(pid ==0 ) {
switch(flag) {
case 1:change_dir(argv1);break;
case 2:outputdir(count,argv1);break;
default:find_command(argv1);break;

}
exit(0);
}
// find_command(argv1);
wait(NULL);
}
return EXIT_SUCCESS;
}

这个只是简单的shell,因为管道,后台的机制还不了解,这些功能后续接着加上。

现在说说我写的时候出现的问题。

  1. 使用execvp函数时候,第二个参数的问题我以为只是把将要执行的程序名字敲上去就好了,就直接写的argv[0],结果不停报错,我当时很纳闷。最后查到了这个第二个参数必须是以NULL,结尾的字符指针的数组。最后不得不加一步,让二维数组的地址保存到指针数组中。
  2. 写输出重定向时候,老是提示找不到目录(因为我设定在/usr/bin中查找)。进入到该目录下,发现确实没有“>”,但我的指针数组里有指向“>”的指针,没办法,只好接着写个指针数组将“>”以及“>”这个之后的文件名的指针都屏蔽掉。这才可以执行了。
  3. 输出重定向时候,还有一个问题,就是打开一个新文件,文件中没办法写入数据。执行后总是报出一个“bad file descriptior”。尝试好多次,发现文件是创建了,就是写不进去,出于无奈,百度问度娘,别人给出的解答是,打开的文件没有读写的权限,我恍然大悟,将O_RDWR参数加上。
  4. 刚写完时候,我用cd命令想测一下,看是否可以切换目录,结果发现是不可以的,我就去/usr/bin目录下查找,可是这次却发现,该目录下却是有这个程序的,我猜想是/usr/bin是不能调用的,我就想着改个名字试一下,可改之后,在终端下还是可以用的。因此原因不在这里。我现在想着可能子进程切换目录了,但因为时间太短,就又回到原目录下了,我加上sleep函数来延长时间。可是发现也没用,程序的结果是根本就没有执行这个函数,直接退出了。现在想一下确实是这样,因为通过exec函数族打开新的进程之后,原进程直接结束了,所以没有执行sleep函数。那到底是什么问题呢?通过查书,发现父进程vfork的进程,跟父进程是共用的数据段,因此,父进程的环境变量,子进程是没法改变得,所以,cd是调用不了的。后面我就自己写了个cd的函数调用。
  5. 在最后排版时候发现,怎么也排版不了,因为我用的是vfork,所以当时完全对父子进程的顺序不担心,可是怎么调试,结果都跟fork的结果一样,先是父进程进入到了下一层循环,打印出了shell名字,然后是子进程执行的各种结果。调试好久,都没有得到想要的结果,是在没办法,用wait函数将父进程挂起来,等子进程结束。这一下成功了,翻书查看定义,发现vfork调用exec或exit之后,父进程可能被调度运行。错出在这里。

通过myshell的实现,才发现有这么多的问题需要注意,庆幸组长最后让我写这个程序了(原来没要求必须写),通过自己去思考,自己实现,发现其中有很多的欢乐。哈哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值