c - tail exec 在管道中不能很好地工作(直到父进程死亡才输出!)

标签 c linux unix io pipe

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

int pipFd[1000][2], hasPipe, forked, pipNum = 0, pNum = 0, bPipe = 0, bpipFd[2], stPipe, InpToChld = 0;
pid_t pid[1000];
void * outInp;

int builtin_command(char **argv)
{
    if (!strcmp(argv[0], "quit")) /* quit command */
        exit(0);
    if (!strcmp(argv[0], "&")) /* Ignore singleton & */
        return 1;
    return 0; /* Not a builtin command */
}
int parsecmd(char *buf, char **argv)
{
    char *delim;                  /* Points to first space delimiter */
    int argc;                     /* Number of args */
    int bg;                       /* Background job? */
    buf[strlen(buf) - 1] = ' ';   /* Replace trailing '\n' with space */
    while (*buf && (*buf == ' ')) /* Ignore leading spaces */
        buf++;

    /* Build the argv list */
    argc = 0;
    while ((delim = strchr(buf, ' ')))
    {
        argv[argc++] = buf;
        *delim = '\0';
        buf = delim + 1;
        while (*buf && (*buf == ' ' || *buf == '<')) /* Ignore spaces */
            buf++;
    }
    argv[argc] = NULL;

    if (argc == 0) /* Ignore blank line */
        return 1;

    /* Should the job run in the background? */
    if ((bg = (*argv[argc - 1] == '&')) != 0)
        argv[--argc] = NULL;

    return argc;
}

void myExec(char **argv, char *buf)
{
    // if ((pid[pNum] = fork()) == 0)
    // {
        strcpy(buf, "/bin/");
        buf[5] = 0;
        strcat(buf, argv[0]);
        //printf("%s\n", buf);
        if (execv(buf, argv) < 0)
        {
            memset(buf, 0, 255);
            strcpy(buf, "/usr/bin/");
            strcat(buf, argv[0]);

            if (execv(buf, argv) < 0)
            {
                printf("exec failed\n");
                exit(-1);
            }
        }
        exit(0);
    // }
    // else
    //     wait(NULL);
}

int splitPipe(char **cmdLine)
{
    static char *svBuftok;

    if (!hasPipe)
        *cmdLine = strtok_r(*cmdLine, "|", &svBuftok);
    else{
    //printf("--------%s\n", svBuftok);
        *cmdLine = strtok_r(svBuftok, "|", &svBuftok);
    }
    //printf(".......................%s %s\n", svBuftok, *cmdLine);
    return strlen(svBuftok);
}

int isDigit(char * strings){
    int i, tmp = strlen(strings);
    for(i = 0; i < tmp; i++){
        if(strings[i] < '0' || strings[i] > '9') return 0;
    }
    return 1;
}


void handler(int sig)
{
    if (sig == SIGINT && forked) exit(0);
}

static char *getcmd()
{
    static char buf[256];

    //fputs("> ", stdout);
    //printf("asdfasdfasdf\n");
    fflush(stdin);
    fflush(stdout);
    if (fgets(buf, sizeof(buf), stdin) == NULL)
        return NULL;

    if (buf[strlen(buf)] == '\n')
        buf[strlen(buf)] = 0;

    return buf;
}

int main()
{

    char *cmdline;
    char *argv[12000];
    char c, dir[256], buf[256], rdBuf[100000], pipBuf[100000];
    int status, fd, rfd, dest, argc;
    pid_t tmpPid;
    getcwd(dir, 256);
    signal(SIGINT, handler);
    signal(SIGTSTP, handler);
    signal(SIGCHLD, handler);
    signal(30, handler);
    //outInp = &&Outinp;
//printf("gd\n");
    while (cmdline = getcmd())
    {
ret:
        do
        {
            rfd = 0;
            hasPipe = splitPipe(&cmdline);
            //printf(":::::::::::::::%s %d\n", cmdline, hasPipe);
            if (strlen(cmdline) <= 1)
                continue;
            argc = parsecmd(cmdline, argv);
            if (!builtin_command(argv))
            {
                if(!strcmp(argv[0], "exit")) exit(0);
                {
                    if(hasPipe) pipe(pipFd[pNum]);
                    if(!bPipe) pipe(bpipFd);
                    fflush(NULL);
                    if((pid[pNum] = fork()) == 0){
                        int ofd, svStdout = dup(1), svStin = dup(0);
                        forked = 1;
                        close(pipFd[pNum][0]);
                        //printf("%s %d\n",argv[0], getpid());
                        //fflush(stdout);
                        //printf("\n");
                        if(bPipe) {
                            close(pipFd[pNum - 1][1]);
                            dup2(pipFd[pNum - 1][0], STDIN_FILENO); 
                        }
                        else{
                            close(bpipFd[1]);
                            dup2(bpipFd[0], STDIN_FILENO);
                        }

                        //addArgv(pipBuf, &argc, argv);
                        if(hasPipe) dup2(pipFd[pNum][1], 1);

                        if(!strcmp(argv[argc - 2], ">")){
                            //printf("chked %s\n", argv[argc - 1]);
                            remove(argv[argc - 1]);
                            ofd = open(argv[argc - 1], O_WRONLY | O_CREAT, 0755);
                            dup2(ofd, 1);
                            argc -= 2;
                            argv[argc] = NULL;
                        }
                        else if(!strcmp(argv[argc - 2], ">>")){
                            //printf("chked %s\n", argv[argc - 1]);
                            ofd = open(argv[argc - 1], O_WRONLY);
                            dup2(ofd, 1);
                            argc -= 2;
                            argv[argc] = NULL;
                        }

                        fflush(stdout);
                            myExec(argv, buf);

                        close(pipFd[pNum][1]);

                        if(bPipe) {
                            close(pipFd[pNum - 1][0]);
                        }
                        else{
                            close(bpipFd[0]);
                        }
                        dup2(svStin, 0);
                        dup2(svStdout, 1);
                        if(!strcmp(argv[argc - 2], ">")) close(ofd);
                        exit(0);
                    }
                    else{
                        if(!bPipe) {
                            close(bpipFd[0]);
                            stPipe = pid[pNum];
                            InpToChld = 1;
                        }
                        pNum++;

                    }
                }

                bPipe = hasPipe;
            }
        }while (hasPipe);
        while(InpToChld){
            memset(rdBuf, 0, sizeof(rdBuf)); int i;
            fflush(NULL);
            //printf("Inp~~\n");
            if(read(0, rdBuf, sizeof(rdBuf)) == 0){
                write(bpipFd[1], "\0", 1);
                InpToChld = 0;
                break;
            }

            if(write(bpipFd[1], rdBuf, strlen(rdBuf)) < 0){
                cmdline = rdBuf;
                InpToChld = 0;
                goto ret;
            }
            fflush(NULL);
                //fflush(stdout);
        }
    }
}

首先,exec to tail 效果很好。

但是 tail 的输出直到父进程死亡才被打印。

例如,cat < /proc/meminfo | head效果很好

但是cat < /proc/meminfo | tail在退出父级后打印。

我猜这是关于输入/输出问题,但我无法解决这个问题。

最佳答案

编写小型 shell 的步骤:

  1. 处理SIGCHLD
  2. 如有必要,处理重定向
  3. 如果需要的话处理pipe

以下代码可以工作,但不能处理内置命令:

#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

void signal_SIGCHLD_handle(int sig) {
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}

int main()
{
    char buf[256];

    while (fgets(buf, sizeof(buf), stdin) != NULL) {
        if (fork() > 0) { // parent
            wait(NULL);
            continue;
        }
        //child
        // handle SIGCHLD
        struct sigaction act;
        act.sa_handler = signal_SIGCHLD_handle;
        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_RESTART;
        sigaction(SIGCHLD, &act, NULL);

        if (buf[strlen(buf)] == '\n')
            buf[strlen(buf)] = '\0';

        for (char* next_cmd = strtok(buf, "|"); next_cmd != NULL; ) {
            char* current_cmd = next_cmd;
            next_cmd = strtok(NULL, "|");

            char* argv[10];
            int argv_index = 0;
            int new_argv = 1;
            for (;;) {
                if(*current_cmd == '\0') {
                    argv[argv_index] = NULL;
                    break;
                }
                if (isspace(*current_cmd)) {
                    *current_cmd++ = '\0';
                    new_argv = 1;
                    continue;
                }
                if (*current_cmd == '<') {
                    ++current_cmd;
                    while (isspace(*current_cmd))
                        ++current_cmd;
                    if (*current_cmd == '\0') {
                        printf("Please use cmd < file_name");
                        return -1;
                    }
                    char* filename = current_cmd;
                    while (!isspace(*current_cmd) && *current_cmd != '\0')
                        ++current_cmd;
                    if (*current_cmd != '\0')
                        *current_cmd++ = '\0';
                    int fd = open(filename, O_RDONLY);
                    if (fd < 0) {
                        perror("<");
                        return -1;
                    }
                    dup2(fd, 0);
                    close(fd);
                    continue;
                }
                if (*current_cmd == '>') {
                    int add = 0;
                    if (*++current_cmd == '>') {
                        add = 1;
                        ++current_cmd;
                    }
                    while (isspace(*current_cmd))
                        ++current_cmd;
                    if (*current_cmd == '\0') {
                        printf(add == 0 ? "Please use cmd > file_name" : "Please use cmd >> file_name");
                        return -1;
                    }
                    char* filename = current_cmd;
                    while (!isspace(*current_cmd) && *current_cmd != '\0')
                        ++current_cmd;
                    if (*current_cmd != '\0')
                        *current_cmd++ = '\0';
                    int fd = open(filename, add == 0 ? (O_WRONLY|O_CREAT) : (O_WRONLY|O_CREAT|O_APPEND), 0644);
                    if (fd < 0) {
                        perror(add == 0 ? ">" : ">>");
                        return -1;
                    }
                    dup2(fd, 1);
                    close(fd);
                    continue;
                }
                if (new_argv == 1) {
                    new_argv = 0;
                    argv[argv_index++] = current_cmd;
                }
                ++current_cmd;
            }
            if (argv_index == 0)
                continue;
            if (next_cmd != NULL) {
                int pipe_fd[2];
                pipe(pipe_fd);
                if (fork() == 0) {
                    close(pipe_fd[0]);
                    dup2(pipe_fd[1], STDOUT_FILENO);
                    close(pipe_fd[1]);
                    execvp(argv[0], argv);
                    return -1;
                }
                close(pipe_fd[1]);
                dup2(pipe_fd[0], STDIN_FILENO);
                close(pipe_fd[1]);
                continue;
            }
            execvp(argv[0], argv);
        }
    }
    return 0;
}

关于c - tail exec 在管道中不能很好地工作(直到父进程死亡才输出!),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53140454/

相关文章:

linux - linux 程序的帮助在哪里?

Android - 读取 "device attribute"失败并出现错误 "invalid length"

git - 与 git rm -r [文件名] 相关的递归意味着什么

c - 我应该使用 2 个原型(prototype),还是只使用 1 个?

c - char *derp[20] 是什么意思?

c - 执行的线程多于创建的线程

regex - sed regex 到非贪婪替换?

c - C 中的 malloc : same sizeof before and after?

linux - 以 root 身份运行的应用程序,创建任何人都可以读取的日志

bash - vim:如果为空,则将第二列空白替换为同一列中的值