c - 我的 shell 突然退出循环

标签 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

您的单管道代码块是:

/* 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");
    }
}

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

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

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

<小时/>

通过 2

I edited it like so http://pastebin.com/yyMBnKPu Now it doesn't terminate but runs cat a second time and then hangs.

您使用什么编译选项?我总是很挑剔地编译我的 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 行左右。

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

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

解决这些问题后,您已经注释掉了将 redir_inredir_out 设置为已知值 (0 code> 或 FALSE),因此不必输入管道搜索代码。修复后,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 行注释掉导致您看到的错误行为的代码。删除该代码块。

<小时/>

future 的问题

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

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

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

关于c - 我的 shell 突然退出循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42034242/

相关文章:

c - 读取子进程中的管道卡在第二次读取上

grails - Grails日志记录在 fork 模式下不起作用

java - JNI 和访问子类

c - 使用 NULL 强制结束可变函数参数

linux - 如何保持与 paramiko 的 session

linux - 逐行读取 awk 和 if

c# - 访问违规异常

c - 在 C 源代码中返回无处(?)

bash - FFmpeg 脚本跳过文件

c - 在c中用信号量同步两个子进程