【问题标题】:My shell suddenly exits out of loop我的外壳突然退出循环
【发布时间】:2017-02-03 22:48:23
【问题描述】:

所以这段代码在管道命令之后终止:例如cat text.txt | wc。它可以打开文本文件并计算字数,但它不会返回菜单,而是终止。有什么想法吗?

#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>
#include <fcntl.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#define LOGOUT 20
#define MAXNUM 40
#define MAXLEN 160
#define MAXSIZE 100
#define BUFSIZE 20
#define TRUE 1
#define FALSE 0


typedef struct command_history
{
    char *command;
    struct command_history *next;
} history;

/* Main */

int main(void)
{
    char *currentpath, *cmd, line[MAXLEN], *args[MAXNUM], *args2[MAXNUM], *args3[MAXNUM];
    int background, i, j, t, wildcard, opts;
    int redir_in, redir_out;
    int fd;
    long size;
    char *buf;
    pid_t pid, pid2;

    char *history[MAXSIZE], command[MAXSIZE], command_number[MAXSIZE], *temp;
    int history_count = 0, length, number, offs;
    int pipefd[2], pipefd2[2];
    int numberofpipes, pipesuccess;
    char buffer[BUFSIZE];
    FILE *fp;

    buf = (char*)malloc((size_t)size);

    /* set the signal handler for alarm */

    /*get default working directory as current path*/
    currentpath = getenv("PWD");

    while (1)
        {
            /* initialize */

            background = FALSE;
            opts = FALSE;
            redir_in = FALSE; redir_out = FALSE;
            pipesuccess = FALSE;
            numberofpipes = 0, t = 0, offs = 0;

            /* print the prompt */
            fprintf(stdout, "%s > ", currentpath);

            /* set the timeout for autologout, function alarm() */

            /* read the users command */
            if (fgets(line,MAXLEN,stdin) == NULL) {
                fprintf(stdout, "\nlogout\n");
                exit(0);
            }

            line[strlen(line) - 1] = '\0';

            if (strlen(line) == 0)
                continue;

            /* start to background? (check if the last character is '&') */
            if(line[strlen(line)-1] == '&')
                {
                    line[strlen(line)-1] = '\0'; //remove '&'
                    background = TRUE;
                }

            /* saving the user command into history list */

            if(line[0] != '!'){
                if (history_count < MAXSIZE){
                    history[history_count] = strdup(line);
                    history_count++;
                }
            }


            /* split the command line to args[]*/
            i = 0;  //number of arguments
            cmd = line;
            while((args[i] = strtok(cmd, " ")) != NULL)
                {
                    i++; //argument count
                    cmd = NULL;
                }

            /* history usage */
            if(line[0] == '!')
                {
                    // find right command index from history
                    j = 0;
                    while (isdigit(line[j+1])){
                        command_number[j] = line[j+1];
                        j++;
                    }
                    number = atoi(command_number);
                    if (number <= history_count)
                        {
                            // parsing the history commands back into args
                            t = 0;
                            temp = history[number-1];
                            while((args[t] = strtok(temp, " ")) != NULL)
                                {
                                    t++;
                                    temp = NULL;
                                }
                        }
                    else
                        {
                            printf("Out of range of array.\n");
                        }
                }

            /* find and open redirected files*/
            for(j = 0; j < i; j++)
                {
                    if(strcmp(args[j], ">") == 0){
                        args[j] = NULL;
                        fd = open(args[j+1], O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
                        redir_out = TRUE;
                        if (fd < 0) {
                            perror("open");
                            exit(0);
                        }
                    }
                    else if(strcmp(args[j], "<") == 0){
                        args[j] = NULL;
                        fd = open(args[j+1], O_RDONLY, S_IRUSR | S_IWUSR);
                        redir_in = TRUE;
                        if (fd < 0) {
                            perror("open");
                            exit(0);
                        }
                    }
                }

            /* exit? */
            if (strcmp(args[0], "exit") == 0) {
                exit(0);
            }

            /*  cd  */
            if (strcmp(args[0], "cd") == 0)
                {
                    if(i > 1)
                        {
                            if(chdir(args[1]) != 0) //change to inputted directory
                                {
                                    perror("chdir");
                                }
                            currentpath = getcwd(buf, (size_t)size); //set currentpath to cwd
                        }
                    else
                        {
                            currentpath = getenv("HOME"); // change to homedir
                        }

                    continue;
                }

            /* history */
            if(strcmp(args[0], "history") == 0)
                {
                    printf("Command history: \n");
                    for (j = 0; j < history_count; j++) {
                        printf("[%d]  %s \n", j+1, history[j]);
                    }
                    continue;
                }

            /* find pipe marks for single and double pipe*/
            int k=0, z=0, y=0;
            if(!redir_out && !redir_in){
                for (j = 0; j < i; j++) //i == argument count
                    {
                        if(strncmp(args[j], "|", 1) == 0){
                            args[j] = NULL; // make it null, so exec will stop there
                            numberofpipes++;
                            continue;
                        }
                        if(numberofpipes == 1){
                            args2[z] = args[j]; //copy other part of args to args2
                            args2[z+1] = NULL; //make sure last args2 is (null)
                            z++;
                            /*  continue; */
                        }
                        /* if(numberofpipes == 2){
                           args3[y] = args[j]; //copy other part of args to args3
                           args3[y+1] = NULL; //make sure last args3 is (null)
                           y++;
                           } */
                    }
            }

            /* single pipe */
            if(numberofpipes == 1){
                pipe(pipefd);
                if((pid = fork()) == -1){
                    perror("fork");
                    exit(1);
                }
                if(pid == 0) { /* child, args2 */
                    close(pipefd[0]);
                    dup2(pipefd[1], STDOUT_FILENO);
                    close(pipefd[1]);
                    execvp(args[0], args);
                }
                else {  /* parent, args */
                    close(pipefd[1]);
                    dup2(pipefd[0], STDIN_FILENO);
                    close(pipefd[0]);
                    execvp(args2[0], args2);
                    perror("execv");
                }
            }
            /* double pipe */
            else if(numberofpipes == 2){
                pipe(pipefd);
                pipe(pipefd2);

                if((pid = fork()) == -1){
                    perror("fork");
                    exit(1);
                }
                if(pid == 0) {
                    if((pid2 = fork()) == -1){
                        perror("fork 2");
                        exit(1);
                    }
                    if(pid2 == 0){ /* grandchild, args3 */
                        close(pipefd[0]);
                        close(pipefd[1]);
                        close(pipefd2[1]);
                        dup2(pipefd2[0], STDIN_FILENO);
                        close(pipefd2[0]);
                        execvp(args3[0], args3);
                        perror("execv");
                    }
                    else { /* child, args2 */
                        close(pipefd[1]);
                        dup2(pipefd[0], STDIN_FILENO);
                        close(pipefd[0]);
                        close(pipefd2[0]);
                        dup2(pipefd2[1], STDOUT_FILENO);
                        close(pipefd2[0]);
                        execvp(args2[0], args2);
                        perror("execv");
                    }
                }
                else{ /* parent, args */
                    close(pipefd[0]);
                    dup2(pipefd[1], STDOUT_FILENO);
                    close(pipefd[1]);
                    execvp(args[0], args);
                }
            }

            /* fork to run the command */
            switch (pid = fork()) {
            case -1:
                /* error */
                perror("fork");
                continue;
            case 0:/* child process, exec() */

                if(redir_out){ //redirection to output file
                    dup2(fd, STDOUT_FILENO);
                    close(fd);
                }
                else if(redir_in){ //redirection to input file
                    dup2(fd, STDIN_FILENO);
                    close(fd);
                }


                execvp(args[0], args);
                perror("execv");
                exit(1);

            default:
                /* parent (shell), wait() if not background process */
                if(!background){
                    alarm(0);
                    while(wait(NULL) != pid)
                        {
                            printf("please.");
                        }
                }
                break;
            }

        }
    //globfree(&globbuf);
    free(buf);
    return 0;
}

【问题讨论】:

  • 寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定的问题或错误以及在问题本身中重现它所需的最短代码。没有明确问题陈述的问题对其他读者没有用处。请参阅:如何创建最小、完整和可验证的示例。
  • 我打赌这与exit 语句的行有关

标签: c shell fork pipeline


【解决方案1】:

通过 1

您的单管道代码块是:

/* single pipe */
if(numberofpipes == 1){
    pipe(pipefd);
    if((pid = fork()) == -1){
        perror("fork");
        exit(1);
    }
    if(pid == 0) { /* child, args2 */
        close(pipefd[0]);
        dup2(pipefd[1], STDOUT_FILENO);
        close(pipefd[1]);
        execvp(args[0], args);
    }
    else {  /* parent, args */
        close(pipefd[1]);
        dup2(pipefd[0], STDIN_FILENO);
        close(pipefd[0]);
        execvp(args2[0], args2);
        perror("execv");
    }
}

您的父进程执行管道的两个部分之一;当然它会在管道终止时终止。

您需要在沿线的某个地方再次分叉。您可以让父进程(主 shell)依次分叉每个子进程,也可以让父进程分叉一次,然后第一个子进程负责创建管道。第一个的优点是父 shell 可以找到管道中每个进程的退出状态——这就是 Bash 所做的。第二种的优点是它更像是旧shell所做的。第一个子进程执行管道中的最后一个进程,其退出状态控制着整个管道的退出状态。

使用第二种方法,第一个孩子创建管道,而父母不必担心关闭它们。对于第一种方法,父进程必须确保在等待任何退出之前关闭管道。


通过 2

我像这样编辑它http://pastebin.com/yyMBnKPu 现在它不会终止而是再次运行 cat 然后挂起。

您使用哪些编译选项?我总是很挑剔地编译我的 C 代码,最后也用挑剔的选项来编译 SO 的答案。使用 PasteBin 中的代码(470 多行——在问题中不包括所有这些是合理的),我得到:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
>     -Wold-style-definition sh61.c -o sh61 
sh61.c:30:6: error: no previous prototype for ‘sighandler’ [-Werror=missing-prototypes]
 void sighandler(int sig)
      ^~~~~~~~~~
sh61.c: In function ‘sighandler’:
sh61.c:30:21: error: unused parameter ‘sig’ [-Werror=unused-parameter]
 void sighandler(int sig)
                     ^~~
sh61.c: At top level:
sh61.c:38:6: error: no previous prototype for ‘left_child’ [-Werror=missing-prototypes]
 void left_child(char *args[MAXNUM], int pipefd[2])
      ^~~~~~~~~~
sh61.c:49:6: error: no previous prototype for ‘right_child’ [-Werror=missing-prototypes]
 void right_child(char *args2[MAXNUM],int pipefd[2])
      ^~~~~~~~~~~
sh61.c:59:5: error: no previous prototype for ‘SinglePipe2’ [-Werror=missing-prototypes]
 int SinglePipe2(char *args[MAXNUM], char *args2[MAXNUM], int redir_in, int redir_out, int fd){
     ^~~~~~~~~~~
sh61.c: In function ‘SinglePipe2’:
sh61.c:115:13: error: this ‘if’ clause does not guard... [-Werror=misleading-indentation]
             if (execvp(args[0], args))
             ^~
sh61.c:117:5: note: ...this statement, but the latter is misleadingly indented as if it is guarded by the ‘if’
     exit(1);
     ^~~~
sh61.c: In function ‘main’:
sh61.c:367:19: error: "/*" within comment [-Werror=comment]
    if(pid == 0) { /* child, args2

sh61.c:374:26: error: "/*" within comment [-Werror=comment]
    else {  parent, args  /*

sh61.c:397:21: error: "/*" within comment [-Werror=comment]
      if(pid2 == 0){ /* grandchild, args3

sh61.c:406:13: error: "/*" within comment [-Werror=comment]
      else { /* child, args2

sh61.c:417:10: error: "/*" within comment [-Werror=comment]
    else{ /* parent, args

sh61.c:428:5: error: "/*" within comment [-Werror=comment]
     /* error

sh61.c:431:11: error: "/*" within comment [-Werror=comment]
    case 0:/* child process, exec()

sh61.c:456:5: error: "/*" within comment [-Werror=comment]
     /* parent (shell), wait() if not background process

sh61.c:332:7: error: unused variable ‘k’ [-Werror=unused-variable]
   int k=0, z=0, y=0;
       ^
sh61.c:142:8: error: unused variable ‘fp’ [-Werror=unused-variable]
  FILE *fp;
        ^~
sh61.c:141:7: error: unused variable ‘buffer’ [-Werror=unused-variable]
  char buffer[BUFSIZE];
       ^~~~~~
sh61.c:140:21: error: variable ‘pipesuccess’ set but not used [-Werror=unused-but-set-variable]
  int numberofpipes, pipesuccess;
                     ^~~~~~~~~~~
sh61.c:139:17: error: unused variable ‘pipefd2’ [-Werror=unused-variable]
  int pipefd[2], pipefd2[2];
                 ^~~~~~~
sh61.c:139:6: error: unused variable ‘pipefd’ [-Werror=unused-variable]
  int pipefd[2], pipefd2[2];
      ^~~~~~
sh61.c:138:25: error: unused variable ‘length’ [-Werror=unused-variable]
  int history_count = 0, length, number, offs;
                         ^~~~~~
sh61.c:137:26: error: unused variable ‘command’ [-Werror=unused-variable]
  char *history[MAXSIZE], command[MAXSIZE], command_number[MAXSIZE], *temp;
                          ^~~~~~~
sh61.c:136:19: error: unused variable ‘act’ [-Werror=unused-variable]
  struct sigaction act;
                   ^~~
sh61.c:130:6: error: variable ‘background’ set but not used [-Werror=unused-but-set-variable]
  int background, i, j, t, wildcard, opts;
      ^~~~~~~~~~
sh61.c:129:73: error: variable ‘args3’ set but not used [-Werror=unused-but-set-variable]
  char *currentpath, *cmd, line[MAXLEN], *args[MAXNUM], *args2[MAXNUM], *args3[MAXNUM];
                                                                         ^~~~~
cc1: all warnings being treated as errors
$

第 115ff 行的问题是:

        if (execvp(args[0], args))
            perror("execv");
            exit(1);

perror()if 控制,但exit() 无条件发生。不过由于execvp()只有失败才会返回,所以不需要测试:

        execvp(args[0], args);
        perror("execv");
        exit(1);

未使用的变量很痛苦。错误嵌套的 cmets 是一个不好的迹象。如果要消除代码块,请在它们之前使用#if 0,在它们之后使用#endif。使用这些函数是个好主意。使用 VCS(版本控制系统)是个好主意。使用 cmets 消除代码块是一个糟糕的想法。注释掉的代码消失后,文件减少到 370 行左右。

未声明的函数警告来自我对严格原型等的坚持。除了main() 之外的所有函数都是static(只能从它们写入的文件中访问)除非有一个声明它们的头文件,并且头文件是在定义函数的文件和使用函数的所有文件中使用。就个人而言,我认为这是非常有益的。

处理未使用的变量非常简单。注意you're not supposed to use printf() in a signal handler

解决了这些问题后,有一个问题是您已注释掉将 redir_inredir_out 设置为已知值(0FALSE)的代码,因此管道搜索代码不是必然进入。修复后,SinglePipe2() 中的代码如下所示:

    close(pipefd[0]);       /* close parent's copy of pipe */
    close(pipefd[1]);

    while ((ret = wait(&status)) > 0)       /* wait for children */
    {
        if (ret == left_pid)
            printf("left child %d terminated, status: 0x%.4X\n", ret, status);
        else if (ret == right_pid)
            printf("right child %d terminated, status: 0x%.4X\n", ret, status);
        else
            printf("yow! unknown child %d terminated, status %x\n",
                   ret, status);
    }

#if 0
    switch (pid = fork())
    {
    case -1:

        perror("fork");

    case 0:        /* child process, exec() */

        if (redir_out)         // redirection to output file
        {
            dup2(fd, STDOUT_FILENO);
            close(fd);
        }
        else if (redir_in)         // redirection to input file
        {
            dup2(fd, STDIN_FILENO);
            close(fd);
        }

        execvp(args[0], args);
        perror("execv");
        exit(1);
    }
#endif /* 0 */

除非您的非工作代码没有 #if 0#endif 行注释掉导致您看到的不当行为的代码。删除该代码块。


未来的问题

请注意,您的重定向处理代码是不够的。写是合法的:

cat < sh61.c | wc -l > wc.out

在左侧子进程上具有输入重定向,在右侧子进程上具有输出重定向。然而,这是一个未来的问题。您的 MCVE (Minimal, Complete, Verifiable Example) 不应该真正包含“历史”处理和 I/O 重定向处理代码——您还不必担心这些部分。可以说,cd 命令也应该被忽略。我可以看到保留 exit 代码的原因,尽管 shell 也可以干净地处理 EOF。

【讨论】:

  • 好吧,我像这样编辑它pastebin.com/yyMBnKPu 现在它不会终止,而是第二次运行 cat 然后挂起。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-17
  • 1970-01-01
  • 2015-08-07
  • 1970-01-01
  • 2014-02-21
  • 1970-01-01
相关资源
最近更新 更多