C : Redirect child process output to other child process input and stdout

标签 c linux process pipe exec

我是用 C 编写 Linux 编程的新手(我搜索了类似的线程但没有帮助),所以我遇到了以下问题:

我想在 C 语言中为 Linux 创建一个 shell(使用 fork()、exec()、pipe(),它从终端标准输入获取带有参数和管道的命令作为输入(例如“sort foo | uniq - c | wc -l"), 它执行它然后请求下一个命令等。

我将不同的命令、它们的参数等分开,我为每个创建了 1 个子进程,但我无法将每个子进程的输出链接到下一个子进程的输入(以及终端中 stdout 的最后一个输出).

任何人都可以帮助进行正确的管道安装和运行吗??

如需了解更多信息,请询问... 提前致谢

完整代码如下:

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

#define P_READ 0
#define P_WRITE 1

pid_t pID, chID;
char input[100];
char * params[100][100];
char * funcs[100];
char * par;
char * fun;
int i, j, k, stat, infd[2], outfd[2];


//Pipe read
void read_en(int * infd)
{
    dup2(infd[P_READ], STDIN_FILENO);
    close(infd[P_READ]);
    close(infd[P_WRITE]);
}

//Pipe write
void write_en(int * outfd)
{
    dup2(outfd[P_WRITE], STDOUT_FILENO);
    close(outfd[P_READ]);
    close(outfd[P_WRITE]);
}

//Fork, read from pipe, write to pipe, exec   
void fork_chain(int * infd, int * outfd, int i)
{
        pID = fork();

        if (pID == 0)
        {
            if (infd != NULL)
            {
                read_en(infd);
            }

            if (outfd != NULL)
            {
                write_en(outfd);
            }

            execvp(params[i][0], params[i]);
            fprintf(stderr, "Command not found!\n");
            exit(1);
        }
        else if (pID < 0)
        {
            fprintf(stderr, "Fork error!\n");
            exit(1);
        }
        else
        {
            chID = waitpid(-1, &stat, 0);
        }
}


int main()
{
    printf("\n$");
    fgets(input, sizeof(input), stdin);
    strtok(input, "\n");


    while (strcmp(input, "exit") != 0)
    {
        //Separate each command
        k = 0;
        fun = strtok(input, "|");

        while (fun != NULL)
        {
            funcs[k] = fun;
            fun = strtok(NULL, "|");
            k++;
        }


        //Separate each command's parameters
        for (i = 0; i < k; i++)
        {
            j = 0;
            par = strtok(funcs[i], " ");

            while (par != NULL)
            {
                params[i][j] = par;
                par = strtok(NULL, " ");
                j++;
            }
            params[i][j] = NULL;
        }

        //Fork, pipe and exec for each command
        for (i = 0; i < k; i++)
        {
            if (i == 0)
            {
                pipe(outfd);
                fork_chain(NULL, outfd, 0);
                infd[P_READ] = outfd[P_READ];
                infd[P_WRITE] = outfd[P_WRITE];
            }
            else if (i == k-1)
            {
                fork_chain(infd, NULL, 1);
                close(infd[P_READ]);
                close(infd[P_WRITE]);
            }
            else
            {
                pipe(outfd);
                fork_chain(infd, outfd, i);
                close(infd[P_READ]);
                close(infd[P_WRITE]);
                infd[P_READ] = outfd[P_READ];
                infd[P_WRITE] = outfd[P_WRITE];
            }
        }

        //Ask for next input
        printf("\n$");
        fgets(input, sizeof(input), stdin);
        strtok(input, "\n");
    }

    return (0);
}

最佳答案

shell 进程必须关闭管道的写入端,否则子进程在调用 read() 时不会在其输入上获得 EOF 并永远阻塞。您的代码将关闭写入端,但对于最后一个子进程来说为时已晚,因为 fork_chain() 在返回之前等待它退出(如果管道缓冲区已满,这会带来另一个问题)。如果我们在正确的时间关闭写入端,则必须从 read_en() 中删除 close(infd[P_WRITE]),因为届时它已经关闭.
另外,由于代码乘法在

            else if (i == k-1)
            {
                fork_chain(infd, NULL, 1);

只有当 k 等于 2 时参数 1 才是正确的,在一般情况下它必须是 i

因此,随着 read_en() 的更改,主要的 fork-pipe-and-exec 循环可以重写为

        for (i = 0; i < k; i++)
        {
            fork_chain(i ? infd : NULL, i < k-1 ? pipe(outfd), outfd : NULL, i);
            if (i)
                close(infd[P_READ]);
            if (i < k-1)
                infd[P_READ] = outfd[P_READ],
                close(outfd[P_WRITE]);  // shell is done with it
        }

(注意 infd[P_WRITE] 不再在任何地方使用)。

关于C : Redirect child process output to other child process input and stdout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33940735/

相关文章:

linux - 使用 (S)FTP 访问文件?

javascript - 使用 process.exit() 时并不总是保存 Winston 日志文件 - Node js

php - 使用 PHP 接收和处理传入的电子邮件

c - 为什么 offsetof 返回 size_t 而不是 uintptr_t?

c - 如何在 C 应用程序中处理冲突的结构定义

c - 为什么在 Visual Studio 中取消引用特定地址有时不会导致访问冲突错误?

c - ZLIB 似乎在 C 中返回 CRC32B 而不是 CRC32

c - 将变量参数列表赋予 C 中的被调用函数

linux - 如何将端口 8081 定向到子文件夹

java - 为什么 Java CRLF token 不适用于批处理文件输入?