简体   繁体   English

退出命令在我自己的外壳中无法正常工作

[英]exit command does not work properly in my own shell

I wrote a shell for an assignment and it works correctly, but there is a small run time error which i can not figure out. 我为任务编写了一个Shell,它可以正常工作,但是有一个很小的运行时错误,我无法弄清楚。 When the user enter the command 'exit' through the shell it should come out of newly created shell. 当用户通过外壳输入命令“ exit”时,它应该来自新创建的外壳。 But the problem is I have to type the command 'exit' several times to quit the shell. 但是问题是我必须多次键入命令“ exit”才能退出外壳。 If someone can help me it will be a great pleasure for me! 如果有人可以帮助我,那将是我的荣幸! Thanks all! 谢谢大家!

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

char* cmndtkn[256];
char buffer[256];
char* path=NULL;
char pwd[128];

int main(){

//setting path variable 
    char *env;
    env=getenv("PATH"); 
    putenv(env);

    system("clear");

printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");

while(1){

    fflush(stdin);
    getcwd(pwd,128);
    printf("[MOSH~%s]$",pwd);

    fgets(buffer,sizeof(buffer),stdin);
    buffer[sizeof(buffer)-1] = '\0';

    //tokenize the input command line   
    char* tkn = strtok(buffer," \t\n");
    int i=0;
    int indictr=0;

        // loop for every part of the command
        while(tkn!=NULL)
        {
            if(strcoll(tkn,"exit")==0 ){
                exit(0);                
            }

            else if(strcoll(buffer,"cd")==0){

            path = buffer;
            chdir(path+=3);}

            else if(strcoll(tkn,"|")==0){

            indictr=i;}
            cmndtkn[i++] = tkn;
            tkn = strtok(NULL," \t\n");

        }cmndtkn[i]='\0';

// execute when command has pipe. when | command is found indictr is greater than 0.
    if(indictr>0){

    char* leftcmnd[indictr+1];
    char* rightcmnd[i-indictr];
    int a,b;

        for(b=0;b<indictr;b++)
        leftcmnd[b]=cmndtkn[b];

        leftcmnd[indictr]=NULL;

        for(a=0;a<i-indictr-1;a++)
        rightcmnd[a]=cmndtkn[a+indictr+1];

        rightcmnd[i-indictr]=NULL;

    if(!fork())
    {   
        fflush(stdout);
        int pfds[2];
        pipe(pfds);

            if(!fork()){

                close(1);
                dup(pfds[1]);
                close(pfds[0]);
                execvp(leftcmnd[0],leftcmnd);
                }   
            else{

                close(0);
                dup(pfds[0]);
                close(pfds[1]);
                execvp(rightcmnd[0],rightcmnd);
            }

    }else wait(NULL);

//command not include pipe 

        }else{

        if(!fork()){
            fflush(stdout);
            execvp(cmndtkn[0],cmndtkn);

        }else wait(NULL);

        }

}

}

Like the cd command, the exit command has to be interpreted by the shell as a built-in; cd命令一样, exit命令必须由shell解释为内置命令。 it must exit the loop or call the exit() function directly. 它必须退出循环或直接调用exit()函数。 However, it also appears that should be happening. 但是,这似乎也应该发生。 Note that using strcoll() is a little unusual; 注意,使用strcoll()有点不寻常。 normally, strcmp() is sufficient. 通常, strcmp()就足够了。

You should report problems if execvp() returns — and you must make sure the sub-shell exits so that you don't have multiple shell processes reading the input simultaneously. 如果execvp()返回,则应该报告问题,并且必须确保退出子外壳程序,这样就不会有多个外壳程序进程同时读取输入。 I'm left wondering if this problem is occurring, and that's why you have to type exit multiple times. 我不知道是否会发生此问题,这就是为什么您必须多次键入exit的原因。

You also need to check that fgets() did not report an error. 您还需要检查fgets()没有报告错误。 It always null terminates its input; 它总是null终止其输入; your code does not zap the newline (you'd need strlen(buffer)-1 instead of sizeof(buffer)-1 ). 您的代码不会换行(您需要strlen(buffer)-1而不是sizeof(buffer)-1 )。

The code that reads and sets PATH is wrong. 读取和设置PATH的代码是错误的。 getenv("PATH") returns a pointer to the first character after the PATH= part; getenv("PATH")返回指向PATH=部分后的第一个字符的指针; you then use that to 'set' the environment. 然后使用它来“设置”环境。 Fortunately for you, the average value for PATH does not contain anything that looks like VAR=value , so it is functionally a no-op (though the information is probably copied into the environment, where it makes a mess without causing any major harm). 对您来说幸运的是,PATH的平均值不包含看起来像VAR=value任何内容,因此它在功能上是无操作的(尽管信息很可能被复制到环境中,不会造成任何重大伤害,但会造成混乱) 。

Your code indentation scheme is rococo at best — mostly, it is just woefully inconsistent. 您的代码缩进方案充其量是洛可可式的-大多数情况下,它只是可悲的不一致。 Please be consistent! 请保持一致! The spacing of the lines in the code was also extremely erratic. 代码中的行间距也非常不稳定。 When you're adding code in SO, do not use tabs, do use 4 spaces per indent level, do highlight a block of code that is left justified and use the {} button above the edit box to indent it as code. 在SO中添加代码时,请勿使用制表符,每个缩进级别使用4个空格,突出显示左对齐的代码块,并使用编辑框上方的{}按钮将其缩进为代码。 This also means you don't need to add blank lines to the code. 这也意味着您无需在代码中添加空白行。

You aren't closing enough file descriptors. 您没有关闭足够的文件描述符。 When you use dup() (or dup2() ) to duplicate a pipe to standard input or standard output, you have to close both of the file descriptors returned by pipe() . 当您使用dup() (或dup2() )将管道复制到标准输入或标准输出时,必须关闭pipe()返回的两个文件描述符。

On Linux, using fflush(stdin) is undefined behaviour, AFAIK. 在Linux上,使用fflush(stdin)是未定义的行为AFAIK。 It is defined on Windows, but not on POSIX systems. 它在Windows上定义,但在POSIX系统上未定义。

You don't check that your chdir() system call works. 您无需检查chdir()系统调用是否有效。


Trying your code, I did get one runaway prompt. 尝试您的代码,我确实收到一个失控的提示。 Unfortunately, I couldn't remember or see what triggered the runaway. 不幸的是,我不记得或看不到是什么引发了失控。 The code below is mostly sanitized and seems to behave. 下面的代码大部分经过了清理,并且似乎可以正常运行。 I've annotated some critical changes — and not others. 我已经注释了一些重要的更改,而没有其他注释。 One of the things you should be doing for your own benefit is including trace like the dump_cmd() function so you can see what your program is doing. 为了自己的利益,应该做的一件事是包括dump_cmd()函数之类的跟踪,以便您可以看到程序在做什么。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

char *cmndtkn[256];
char  buffer[256];
char *path = NULL;
char  pwd[128];

static void dump_cmd(char **argv);

int main(void)
{
    /*
    //setting path variable
    char *env;
    env=getenv("PATH");
    putenv(env);
    system("clear");
    */

    printf("\t MY OWN SHELL !!!!!!!!!!\n ");
    printf("_______________________________________\n\n");

    while (1)
    {
        //fflush(stdin);
        getcwd(pwd, 128);
        printf("[MOSH~%s]$", pwd);

        if (fgets(buffer, sizeof(buffer), stdin) == 0)
        {
            putchar('\n');
            break;
        }
        //buffer[sizeof(buffer)-1] = '\0';
        buffer[strlen(buffer)-1] = '\0';

        //tokenize the input command line
        char *tkn = strtok(buffer, " \t\n");
        int i = 0;
        int indictr = 0;

        // loop for every part of the command
        while (tkn != NULL)
        {
            if (strcoll(tkn, "exit") == 0)
            {
                printf("Got: exit\n");
                fflush(stdout);
                exit(0);
            }
            else if (strcoll(tkn, "cd") == 0)   // Was buffer, not tkn
            {
                printf("Got: cd (%s)\n", buffer + 3);
                fflush(stdout);
                path = buffer;
                chdir(path += 3);
            }
            else if (strcoll(tkn, "|") == 0)
            {
                indictr = i;
            }
            cmndtkn[i++] = tkn;
            tkn = strtok(NULL, " \t\n");
        }
        cmndtkn[i] = 0;

        // execute when command has pipe. when | command is found indictr is greater than 0.
        if (indictr > 0)
        {
            char *leftcmnd[indictr+1];
            char *rightcmnd[i-indictr];
            int a, b;

            for (b = 0; b < indictr; b++)
                leftcmnd[b] = cmndtkn[b];

            leftcmnd[indictr] = NULL;

            for (a = 0; a < i-indictr-1; a++)
                rightcmnd[a] = cmndtkn[a+indictr+1];

            rightcmnd[i-indictr-1] = NULL;  // Did not include -1

            if (!fork())
            {
                fflush(stdout);
                int pfds[2];
                pipe(pfds);

                if (!fork())
                {
                    dump_cmd(leftcmnd);
                    close(1);
                    dup(pfds[1]);
                    close(pfds[0]);
                    close(pfds[1]);
                    execvp(leftcmnd[0], leftcmnd);
                    fprintf(stderr, "failed to execvp() %s\n", leftcmnd[0]);
                    exit(1);
                }
                else
                {
                    dump_cmd(rightcmnd);
                    close(0);
                    dup(pfds[0]);
                    close(pfds[0]);
                    close(pfds[1]);
                    execvp(rightcmnd[0], rightcmnd);
                    fprintf(stderr, "failed to execvp() %s\n", rightcmnd[0]);
                    exit(1);
                }
            }
            else
                wait(NULL);

        }
        else
        {
            //command does not include pipe
            if (!fork())
            {
                dump_cmd(cmndtkn);
                fflush(stdout);
                execvp(cmndtkn[0], cmndtkn);
                fprintf(stderr, "failed to execvp() %s\n", cmndtkn[0]);
                exit(1);
            }
            else
                wait(NULL);
        }
    }

    return 0;
}

static void dump_cmd(char **argv)
{
    int i = 0;
    fprintf(stderr, "%d: Command:\n", (int)getpid());
    while (*argv != 0)
        fprintf(stderr, "%d: %d: [[%s]]\n", (int)getpid(), i++, *argv++);
}

I'm not keen on the code, but it does seem mostly sane. 我并不热衷于代码,但是看起来确实很理智。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM