c - 用 C 为操作系统编写 shell 模拟器 : Shell not staying active after multiple commands given

标签 c shell command-line operating-system

<分区>

这些是要求:

  1. 可以执行带参数的命令。
  2. 识别多个管道请求并处理它们。
  3. 识别重定向请求并处理它们。
  4. 键入“exit”退出 shhh shell。

这是我的问题:

每当我执行多个命令(使用 command1 | command2)时,我的 shell 在执行给定的命令后终止,而不是在我的 shell 中等待另一个命令行提示符。该程序应该继续运行,并允许用户输入更多命令,直到输入“退出”。

Code: 
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        #include <unistd.h>
        #include <sys/types.h>
        #include <sys/stat.h>
        #include <fcntl.h>

    int main() {
        // professor-supplied variables for commands and command parsing
        char *path, *argv[20], buf[80], n, *p;
        int m, status, inword, continu;

        // flags for redirection (note: C does not have type bool; using integer value 0 or 1)
        int inputRedirectFlag, outputRedirectFlag, backgroundJobFlag;
        int numArguments, argIndex, argCount, pipes, openPipesCount, pid, directoryIndex;

        // pipes
        int p1[2], p2[2];

        // index for directory set prior to while(1)
        directoryIndex = 0;

        while (1) {


            inword = 0;
            p = buf;
            m = 0;
            continu = 0;
            numArguments = 0;
            argIndex = 0;
            argCount = 0;
            pipes = 0;
            openPipesCount = 0;
            pid = 0;

            // required containers
            int argumentContainer[20] = { 0 };
            char currentDirectory[80] = { 0 };

            // file variables
            char *outputFile = (char *)0;
            char *inputFile = (char *)0;

            // redirection flags
            inputRedirectFlag = 0;
            outputRedirectFlag = 0;
            backgroundJobFlag = 0;

            // SHELL PROMPT
            printf("\nshhh> ");

            // COMMAND PARSING
            while ((n = getchar()) != '\n' || continu)
            {
                /*************************************************
                 required addition to remediate segmentation fault
                *************************************************/
                 if (n == EOF){
                    exit(0);
                }

                if (n == ' ') {
                    if (inword)
                    {
                        inword = 0;
                        *p++ = 0;
                    }
                }
                else if (n == '\n')
                    continu = 0;
                else if (n == '\\' && !inword)
                    continu = 1;
                else {
                    if (!inword)
                    {
                        inword = 1;
                        argv[m++] = p;
                        *p++ = n;
                    }
                    else
                        *p++ = n;
                }
            }

            *p++ = 0;
            argv[m] = 0;

            // capture current working directory
            getcwd(currentDirectory, 80);

            // user wishes to terminate program
            if (strcmp(argv[0], "exit") == 0)
                exit(0);

            // managing input and output redirection and piping
            while (argv[argCount] != 0) {
                if (strcmp(argv[argCount], "<") == 0)
                {
                    inputFile = strdup(argv[argCount + 1]);
                    argv[argCount] = 0;
                    argv[argCount + 1] = 0;
                    inputRedirectFlag = 1;
                }
                else if (strcmp(argv[argCount], ">") == 0)
                {
                    outputFile = strdup(argv[argCount + 1]);
                    argv[argCount] = 0;
                    argv[argCount + 1] = 0;
                    outputRedirectFlag = 1;
                }
                else if (strcmp(argv[argCount], "&") == 0)
                {
                    argv[argCount] = 0;
                    backgroundJobFlag = 1;
                }
                else if (strcmp(argv[argCount], "|") == 0)
                {
                    argv[argCount] = 0;
                    argumentContainer[pipes + 1] = argCount + 1;
                    pipes++;
                }
                else
                    argumentContainer[argCount] = argCount;

                ++argCount;
            }

            // execute commands
            for (int i = 0; i <= pipes; ++i) {
                // pipe and track piping
                if (i < pipes) {
                    pipe(p1);
                    ++openPipesCount;
                }

                /*************************************
                 SWITCH STATEMENT FOR PIPE EXECUTION
                *************************************/

                /*******************************************************
                 Using file flags:
                 O_CREAT: creates file if file does not already exist
                 O_RDONLY: Access mode for file (read only)
                 00700(Mode): read/write/execute permission granted
                 From: http://man7.org/linux/man-pages/man2/open.2.html
                ********************************************************/

                // parent forks child for every exec()
                switch (pid = fork()) {
                    case -1: // process error
                            perror("fork failed");
                            break;

                    case 0: // child process
                            if ((i == 0) && (inputRedirectFlag == 1)) {
                                int input = open(inputFile, O_RDONLY | O_CREAT);
                                if (input == -1) {
                                printf("Input file failed to open\n");
                                return(EXIT_FAILURE);
                            }
                            close(0);
                            dup(input);
                            close(input);
                            }
                            else if ((i == pipes) && (outputRedirectFlag == 1)) {
                                int output = open(outputFile, O_WRONLY | O_CREAT, 00700);
                                if (output < 0) {
                                printf("Output file failed to open\n");
                                return(EXIT_FAILURE);
                            }
                            close(1);
                            dup(output);
                            close(output);
                        }
                        // EXECUTE COMMAND
                        execvp(argv[argumentContainer[argIndex]], &argv[argumentContainer[argIndex]]);
                        break;

                    default: // parent process
                            if (openPipesCount > 0) {
                                close(p2[0]);
                                close(p2[1]);
                            }
                            p2[0] = p1[0];
                            p2[1] = p1[1];
                            break;
                }
                /*************************************
                 END SWITCH STATEMENT FOR PIPE EXECUTION
                 *************************************/

                // if job is not a run-in-background process, wait for process to complete
                if (backgroundJobFlag == 0)
                    wait((int *)0);
            }

            // user wishes to terminate program
            if (strcmp(argv[0], "exit") == 0)
                exit(0);

            // clear all executed commands
            for (int i = 0; i < 20; ++i)
                argv[i] = 0;

            wait(&status);
        }
    }

第一次更新/更改。多个命令正在运行,但现在我的文件重定向(用于输入)不起作用,还有其他问题:

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

    #ifndef READ
    #define READ 0
    #endif

    #ifndef WRITE
    #define WRITE 1
    #endif

    int main() {
        // professor-supplied variables for commands and command parsing
        char *path, *argv[20], buf[80], n, *p;
        int m, status, inword, continu;
        int inputRedirectFlag, outputRedirectFlag, backgroundJobFlag;
        int numArguments, argIndex, argCount, activeCommands, openPipesCount;
        pid_t pid;
        int directoryIndex, commandArchiveIndex;
        int oldFileDescriptor[2], newFileDescriptor[2];
        directoryIndex = 0;

        while (1) {
            inword = 0;
            p = buf;
            m = 0;
            continu = 0;
            numArguments = 0;
            argIndex = 0;
            argCount = 0;
            activeCommands = 0;
            openPipesCount = 0;
            pid = 0;
            path = (char*)0;

            // required containers
            int argumentContainer[20] = { 0 };
            char currentDirectory[80] = { 0 };

            // archive of all commands
            char *commandArchive[20] = { 0 };

            // redirection flags
            inputRedirectFlag = 0;
            outputRedirectFlag = 0;

            backgroundJobFlag = 0;

            // SHELL PROMPT
            printf("\nshhh> ");

            // COMMAND PARSING
            while ((n = getchar()) != '\n' || continu)
            {
                if (n == ' ') {
                    if (inword)
                    {
                        inword = 0;
                        *p++ = 0;
                    }
                }
                else if (n == '\n')
                    continu = 0;
                else if (n == '\\' && !inword)
                    continu = 1;
                else {
                    if (!inword)
                    {
                        inword = 1;
                        argv[m++] = p;
                        *p++ = n;
                    }
                    else
                        *p++ = n;
                }
            }

            *p++ = 0;
            argv[m] = 0;

            // capture all commands in command archive
            while (argv[argIndex] != 0)
            {
                commandArchive[numArguments] = strdup(argv[argIndex]);
                numArguments++;
                ++argIndex;
            }

            // capture current working directory
            getcwd(currentDirectory, 80);

            // user wishes to terminate program
            if (strcmp(argv[0], "exit") == 0)
                exit(0);

            /*************************
             MANAGE BUILT-IN FUNCTIONS
             **************************/
            // user command is "cd"
            if (strcmp(argv[0], "cd") == 0) {
                if (strcmp(argv[1], ".") == 0){ // this should do nothing as a command
                    // *path = ???
                    break;
                }
                else if (strcmp(argv[1], "..") == 0) { // this should move user up one directory
                    // *path = ???
                     // NEED TO PASS PATH HERE!
                    chdir(currentDirectory);
                }
                else {
                    while (argv[directoryIndex] != 0) {
                        getcwd(currentDirectory, 80);
                        // *path = ???
                        // NEED TO PASS PATH HERE!
                        chdir(argv[directoryIndex]);
                        ++directoryIndex;
                    }
                }
            }

            /********************************************
             MANAGE REDIRECTION AND BACKGROUND PROCESSING
            ********************************************/
            while (argv[argCount] != 0) {
                if (strcmp(argv[argCount], "|") == 0) {
                    argv[argCount] = 0;
                    argumentContainer[activeCommands + 1] = argCount + 1;
                    ++activeCommands;
                }
                else if (strcmp(argv[argCount], "<") == 0) {
                    path = strdup(argv[argCount + 1]);
                    argv[argCount] = 0;
                    argv[argCount + 1] = 0;
                    inputRedirectFlag = 1;
                }
                else if (strcmp(argv[argCount], ">") == 0) {
                    path = strdup(argv[argCount + 1]);
                    argv[argCount] = 0;
                    argv[argCount + 1] = 0;
                    outputRedirectFlag = 1;
                }
                else if (strcmp(argv[argCount], "&") == 0) {
                    argv[argCount] = 0;
                    backgroundJobFlag = 1;
                }
                else {
                    argumentContainer[argCount] = argCount;
                }

                ++argCount;
            }

            // execute commands
            for (int pipeIndex = 0; pipeIndex <= activeCommands; ++pipeIndex) {
                if (pipeIndex < activeCommands) { // if user has entered multiple commands with '|'
                    pipe(oldFileDescriptor);
                    pipe(newFileDescriptor);
                    ++openPipesCount;
                }

                /*************************************
                 SWITCH STATEMENT FOR PIPE EXECUTION
                 *************************************/

                // capture current path
                //path = getenv("PATH");

                /*******************************************************
                 Using file flags:
                 O_CREAT: creates file if file does not already exist
                 O_RDONLY: Access mode for file (read only)
                 0600(Mode): owner can read/write
                ddddddddddddd
                 ********************************************************/

                // parent forks child for every exec()
                switch (pid = fork()) {
                    case -1: // process error
                        perror("fork failed");
                        break;

                    case 0: // child process

                        /****************************************************
                         HAVE CHILD PROCESS MANAGE REDIRECTION
                         ****************************************************/
                        if ((pipeIndex == 0) && (inputRedirectFlag == 1)) {
                            int input = open(path, O_RDONLY | O_CREAT, 0600);
                            if (input == -1) {
                                printf("Input file failed to open\n");
                                return(EXIT_FAILURE);
                            }
                            //close(READ);
                            //dup(input);
                            //close(input);
                            dup2(input, READ);
                            close(input);
                        }
                        else if ((pipeIndex == activeCommands) && (outputRedirectFlag == 1)) {
                            int output = open(path, O_WRONLY | O_CREAT, 0600);
                            if (output < 0) {
                                printf("Output file failed to open\n");
                                return(EXIT_FAILURE);
                            }
                            //close(WRITE);
                            //dup(output);
                            //close(output);
                            dup2(output, WRITE);
                            close(output);
                        }
                        /****************************************************
                         REDIRECTION (IF APPLICABLE) EXECUTED BY CHILD PROCESS
                         ****************************************************/

                        /*******************************************************************************
                         CHILD EXECUTES COMMAND:
                         *******************************************************************************/
                        execvp(argv[argumentContainer[pipeIndex]], &argv[argumentContainer[pipeIndex]]);
                        /*******************************************************************************
                         EXECUTION COMPLETE
                         *******************************************************************************/
                        break;

                    default: // parent process
                        if (openPipesCount > 0) { // previous command
                            //close(READ);
                            //dup(newFileDescriptor[READ]);
                            close(newFileDescriptor[READ]);
                            close(newFileDescriptor[WRITE]);
                        }

                        if (openPipesCount < activeCommands) { // more commands need to be executed
                            close(newFileDescriptor[READ]);
                            dup(newFileDescriptor[WRITE]);
                            close(newFileDescriptor[WRITE]);
                        }

                        oldFileDescriptor[READ] = newFileDescriptor[READ];
                        oldFileDescriptor[WRITE] = newFileDescriptor[WRITE];
                        break;
                }
                /***************************************
                 END SWITCH STATEMENT FOR PIPE EXECUTION
                 **************************************/

                // if job is not a run-in-background process, wait for process to complete
                if (backgroundJobFlag == 0)
                    wait((int *)0);
            }

            // user wishes to terminate program
            if (strcmp(argv[0], "exit") == 0)
                exit(0);

            // clear all executed commands
            for (int i = 0; i < 20; ++i)
                argv[i] = 0;

            wait(&status);
        }
    }

我知道我可能在 argumentContainer[] 或管道方面做错了什么。我只是迷路了。

更新版本(更新 2):

在与我的教授讨论我的代码后,我做了很多更改。它几乎工作。我能够通过管道传输多个命令等。但是,“wc”似乎卡住并等待 EOF (ctrl+d),并且 cd 没有更改目录。想知道我是否需要为“cd”制作一个特例/代码。

代码:

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

#ifndef READ
#define READ 0
#endif

#ifndef WRITE
#define WRITE 1
#endif

int main() {
    /* professor-supplied variables for commands and command parsing */
    char *path, *argv[20], buf[80], n, *p;
    int m, status, inword, continu;

    /* flags for redirection (note: C does not have type bool; using integer value 0 or 1) */
    int inputRedirectFlag, outputRedirectFlag;

    /* variables for piping */
    int count, pipes;
    pid_t pid;

    /* pipes */
    int l_pipe[2], r_pipe[2];

    while (1) {
        inword = m = continu = count = pipes = pid = 0;
        p = buf;

        /* required container for handling arguments */
        int argumentContainer[20] = { 0 };

        /* redirection flags */
        inputRedirectFlag = 0;
        outputRedirectFlag = 0;

        /* shell prompt */
        printf("\nshhh> ");

        /* command parsing */
        while ((n = getchar()) != '\n' || continu)
        {
            if (n == ' ') {
                if (inword)
                {
                    inword = 0;
                    *p++ = 0;
                }
            }
            else if (n == '\n')
                continu = 0;
            else if (n == '\\' && !inword)
                continu = 1;
            else {
                if (!inword)
                {
                    inword = 1;
                    argv[m++] = p;
                    *p++ = n;
                }
                else
                    *p++ = n;
            }
        } /* end of command parsing */

        *p++ = 0;
        argv[m] = 0;

        /* user wishes to terminate program */
        if (strcmp(argv[0], "exit") == 0)
            exit(0);

        /* manage redirection */
        while (argv[count] != 0) {
            if (strcmp(argv[count], "|") == 0) {
                argv[count] = 0;
                argumentContainer[pipes + 1] = count + 1;
                ++pipes;
            }
            else if (strcmp(argv[count], "<") == 0) {
                path = strdup(argv[count + 1]); /* copy string argument (file string) */
                argv[count] = 0;
                argv[count + 1] = 0;
                inputRedirectFlag = 1;
            }
            else if (strcmp(argv[count], ">") == 0) {
                path = strdup(argv[count + 1]); /* copy string argument (file string) */
                argv[count] = 0;
                argv[count + 1] = 0;
                outputRedirectFlag = 1;
            }
            else {
                argumentContainer[count] = count;
            }

            ++count;
        } /* end of redirection management */

        /* execute commands */
        for (int index = 0; index <= pipes; ++index) {
            if (index < pipes) { /* if user has entered multiple commands with '|' */
                pipe(r_pipe); /* no pipe(l_pipe); r_pipe becomes next child's l_pipe */
            }

            /*************************************************************************
             FILE FLAG AND FILE MODE DESCRIPTION AND DETAILS:
             *************************************************************************
             Using file flags:
             O_CREAT: creates file if file does not already exist
             O_REDONLY: Acess mode for file (read only)
             O_WRONLY: Access mode for file (write only)
             0600(Mode): owner can read/write
             From:
             http://www.thinkplexx.com/learn/article/unix/command
             ************************************************************************
             ***********************************************************************/

            /* switch-statement for command execution */
            switch (pid = fork()) {
                /* fork() error */
                case -1: perror("fork failed");
                         break;

                case 0: /* child process manages redirection and executes */
                       if ((index == 0) && (inputRedirectFlag == 1)) {
                           int input = open(path, O_RDONLY , 0600);
                           if (input == -1) {
                               printf("Input file failed to open\n");
                               return(EXIT_FAILURE);
                           }
                           dup2(input, READ);
                           close(input);
                       } /* end of input redirection management */
                       else if ((index == pipes) && (outputRedirectFlag == 1)) {
                           int output = open(path, O_WRONLY | O_CREAT, 0600);
                           if (output < 0) {
                               printf("Output file failed to open\n");
                               return(EXIT_FAILURE);
                           }
                           dup2(output, WRITE);
                           close(output);
                       } /* end of output redirection management */

                       /* command executed */
                       execvp(argv[argumentContainer[index]], &argv[argumentContainer[index]]);

                       /* execvp() fails */
                       printf("execution of command failed\n");

                       break;

                default: /* parent process manages the pipes for child process(es) */
                        if (index > 0) {
                            close(l_pipe[READ]);
                            close(l_pipe[WRITE]);
                        }
                        l_pipe[READ] = r_pipe[READ];
                        l_pipe[WRITE] = r_pipe[WRITE];

                        break;
            } /* end of switch-statement for command execution */
        } /* end of loop for all pipes */

        // user wishes to terminate program
        if (strcmp(argv[0], "exit") == 0) {
            exit(0);
        }

        // clear all executed commands
        for (int i = 0; i < 20; ++i) {
            argv[i] = 0;
        }

        wait(&status);
    }
}

最佳答案

问题出在您的管道处理上。您总是关闭 p2 中的管道,但在循环的第一次,它们还没有被初始化为任何东西。只有 p1 已被初始化。 p2 中的默认值之一可能是 0,这是 STDIN 的文件描述符编号。因此,当您尝试在循环的第一次迭代中关闭 p2 中的管道时,您将关闭父程序的 STDIN

关于c - 用 C 为操作系统编写 shell 模拟器 : Shell not staying active after multiple commands given,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46655656/

相关文章:

shell - 哪个终端命令只获取 IP 地址而不获取其他内容?

sql - 从 linux shell 读取带有 SQL 查询的 CSV 文件

command-line - ffmpeg concat 命令未正确读取输入文件

linux - 为什么我们需要执行权限,尽管我们可以使用 "bash script file"运行任何脚本而不需要它?

c - 未处理的异常

可以覆盖返回的结构吗?

使用 strcmp 将输入表单客户端与服务器端的文件进行比较

c - c中嵌入sql,如何检查记录是否存在

shell - 在 shell 脚本中打印输出的行号

java - 即使 Java 安装和路径变量设置没有问题,我也无法在命令行上运行类文件