簡體   English   中英

如何在 C 程序中動態鏈接 unix 實用程序命令,將子進程的標准輸出連接到父進程的標准輸入?

[英]How can i dynamically chain unix utility commands in C program wiring stdout of child process to stdin of parent process?

在下面的代碼中,我相信我正確地將子進程的 output 連接到父進程的輸入,但我可能錯了。 由於某種原因,當我調用 sort 時,shell 上的信息沒有正確顯示。 例如 sort 應該返回 Makefile combo combo.c combo.o

但是我得到了combo combo.c combo.o makefile

我還必須在父進程中重新運行程序並將所有 arguments 放回程序中(由子進程運行的argv[1]除外)我嘗試將argv字符串解析為另一個數組,但不知道該怎么做才能得到他們都進入下一個程序。 嘗試了execvp ,但出現錯誤,說明execvp正在尋找char * const[]

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

int
main(int argc, char *argv[])
{
    char buf[BUFSIZ];
    char args[20][BUFSIZ];
    int i, n;

    if (argc < 2) {
        fprintf(stderr, "Combo usage requires unix utility command argument\n");
        exit(-1);
    }
    else if (argc == 2) {
        execlp(argv[1], argv[1], NULL);
    }
    else {
        strcpy(args[0], argv[0]);

        for (i = 2; i <= argc - 1; i++)
            strcpy(args[i - 1], argv[i]);
        // for(i = 0; i < argc - 1; i++)
        // printf("%s\n", args[i]);

        pid_t pid;
        int fd[2];

        if (pipe(fd) < 0) {
            perror("pipe failed");
            return 1;
        }

        pid = fork();
        if (pid < 0) {
            perror("fork failed");
            exit(-1);
        }
        else if (pid == 0) {
            dup2(fd[1], STDOUT_FILENO);
            close(fd[1]);
            close(fd[0]);
            execlp(argv[1], argv[1], NULL);
            perror("exec failed");
            exit(-1);
        }
        else {
            wait(0);
            dup2(fd[0], STDIN_FILENO);
            close(fd[1]);
            close(fd[0]);
            execlp("./combo", "./combo", args[1], NULL);
            perror("exec failed");
            exit(-1);
        }
    }

    return 0;
}

好吧,有幾個問題...

正如我在評論中提到的, dup2的參數是相反的。

但是,主要問題是,雖然可以使用單個fork [並從父級執行其中一個子級] 來執行此操作,但最好在循環中執行兩個fork調用。

這樣, wait調用 [復數] 可以由父級完成,而wait不會干擾兩個子級之間的數據流。 這就是大多數 shell 的工作方式[我已經在這里回答了這個問題]。

所以,我不得不稍微重構你的代碼。 它編譯,但我沒有測試它。

請注意,對於給定的 pipe,例如: sender | receiver sender | receiver ,這首先啟動發送者。 然后,啟動接收器。 換句話說,以從左到右的順序啟動程序。

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>

int
main(int argc, char *argv[])
{
    char buf[BUFSIZ];
    char args[20][BUFSIZ];
    int i, n;

    if (argc < 2) {
        fprintf(stderr, "Combo usage requires unix utility command argument\n");
        return 1;
    }

    if (argc == 2) {
        execlp(argv[1], argv[1], NULL);
        perror("execlp");
        return 2;
    }

    strcpy(args[0], argv[0]);

    for (i = 2; i <= argc - 1; i++)
        strcpy(args[i - 1], argv[i]);
    // for(i = 0; i < argc - 1; i++)
    // printf("%s\n", args[i]);

    pid_t pid;
    pid_t pids[2];
    int fd[2];

    if (pipe(fd) < 0) {
        perror("pipe failed");
        return 3;
    }

    // fork/exec our two children
    for (int icld = 0;  icld <= 1;  ++icld) {
        int ipipe = ! icld;

        // do the fork
        pid = fork();

        // more elaborate programs might make use of this -- just to illustrate
        pids[icld] = pid;

        // fork failure ...
        if (pid < 0) {
            perror("fork failed");
            return 4;
        }

        // parent ...
        if (pid != 0)
            continue;

        // connect up pipe to correct end of child
        dup2(ipipe,fd[ipipe]);

        // close the extra pipe units
        close(fd[1]);
        close(fd[0]);

        // execute the child
        switch (ipipe) {
        case 0:  // receiver
            execlp("./combo", "./combo", args[1], NULL);
            break;

        case 1:  // sender
            execlp(argv[1], argv[1], NULL);
            break;
        }

        fprintf(stderr,"execlp failure for child %d/%d -- %s\n",
            icld,ipipe,strerror(errno));
        exit(5);
    }

    close(fd[0]);
    close(fd[1]);

    int status;
    int bigcode = 0;
    int curcode = 0;

    // wait for all children to complete
    while (1) {
        pid = wait(&status);
        if (pid <= 0)
            break;

        do {
            // simple exit code
            if (WIFEXITED(status)) {
                curcode = WEXITSTATUS(status);
                fprintf(stderr,"child %d -- exit %d\n",pid,curcode);
                break;
            }

            // child got a signal [probably SIGSEGV]
            if (WIFSIGNALED(status)) {
                curcode = WTERMSIG(status);
                fprintf(stderr,"child %d -- signal %d\n",pid,curcode);
                curcode + 20;
                break;
            }

            // stopped and/or continued -- we don't really care for this app
            curcode = 19;
        } while (0);

        // remember the most severe error
        if (curcode > bigcode)
            bigcode = curcode;
    }

    return bigcode;
}

對於我創建的簡單 shell [測試],請參閱我的答案: fd leak, custom Shell

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM