0

我为分配编写了一个 shell,它可以正常工作,但是有一个小的运行时错误,我无法弄清楚。当用户通过外壳输入命令“退出”时,它应该来自新创建的外壳。但问题是我必须多次键入命令“退出”才能退出 shell。如果有人可以帮助我,我将非常高兴!谢谢大家!

#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);

        }

}

}
4

1 回答 1

2

cd命令一样,exit命令必须由 shell 解释为内置的;它必须退出循环或exit()直接调用该函数。然而,这似乎也应该发生。请注意,使用strcoll()有点不寻常;通常,strcmp()就足够了。

如果execvp()返回,您应该报告问题——并且您必须确保子 shell 退出,这样您就不会有多个 shell 进程同时读取输入。我想知道这个问题是否正在发生,这就是你必须exit多次输入的原因。

您还需要检查fgets()没有报告错误。它总是 null 终止它的输入;您的代码不会删除换行符(您需要strlen(buffer)-1而不是sizeof(buffer)-1)。

读取和设置的代码PATH是错误的。 getenv("PATH")返回指向该PATH=部分之后的第一个字符的指针;然后你用它来“设置”环境。对您来说幸运的是,PATH 的平均值不包含任何看起来像的内容VAR=value,因此它在功能上是无操作的(尽管信息可能会复制到环境中,在那里它会造成混乱而不会造成任何重大伤害)。

你的代码缩进方案充其量是洛可可式的——大多数情况下,它只是非常不一致。请保持一致!代码中的行间距也非常不稳定。当您在 SO 中添加代码时,不要使用制表符,每个缩进级别使用 4 个空格,突出显示左对齐的代码块并使用{}编辑框上方的按钮将其缩进为代码。这也意味着您不需要在代码中添加空行。

您没有关闭足够的文件描述符。当您使用dup()(或dup2()) 将管道复制到标准输入或标准输出时,您必须关闭由 . 返回的两个文件描述符pipe()

在 Linux 上,使用fflush(stdin)是未定义的行为,AFAIK。它是在 Windows 上定义的,但不是在 POSIX 系统上定义的。

您没有检查您的chdir()系统调用是否有效。


尝试您的代码,我确实得到了一个失控的提示。不幸的是,我不记得或看到是什么引发了失控。下面的代码大部分都经过了清理,并且似乎可以正常运行。我已经注释了一些重要的变化——而不是其他的。为了您自己的利益,您应该做的一件事是包括像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++);
}

我不热衷于代码,但它看起来确实很理智。

于 2013-04-25T04:46:24.240 回答