我的shell突然退出循环

时间:2017-02-03 22:48:24

标签: c shell fork pipeline

因此,此代码在管道命令后终止:例如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;
}

1 个答案:

答案 0 :(得分:2)

通过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)轮流分配每个子进程,或者你可以让父fork一次,然后第一个子进行创建管道。第一个优点是父shell可以找到管道中每个进程的退出状态 - 这就是Bash所做的。第二个的优点是它更像旧壳做的。第一个子进程执行管道中的最后一个进程,其退出状态控制整个管道的退出状态。

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

通过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);

未使用的变量很痛苦。严重嵌套的评论是一个不好的迹象。如果您想要消除代码块,请先使用#if 0,然后使用#endif。使用这些功能是个好主意。使用VCS(版本控制系统)是个好主意。使用注释来消除代码块是一个的想法。随着注释掉的代码消失,文件大小减少到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。