C 程序模拟命令行提示符

标签 c pipe

我正在尝试实现一个小型 C 程序,其执行方式类似于 Linux shell 命令提示符 $ sort < Names.txt | uniq | wc - l 。为此,我使用 execlp 来运行命令

生成的程序将对任意名称列表进行排序并删除重复项。它对列表进行排序,因为它需要相邻的重复行才能被删除。然后只计算行数。

我已经发布了我的代码,它目前在我编译后挂起gcc -o sortuniqwc sortuniqwc.c并运行./sortuniqwc < Names.txt 。如果我注释掉 fd 的管道,每个系统调用似乎都能正确执行。我不确定为什么它没有正确地将进程传递给系统调用

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

int main(int argc, char *arv[])
{
    pid_t pid;
    int fd1[2]; //making file descriptor 1

    if (pipe(fd1) == -1)
    {
        fprintf(stderr, "pipe failed");
        return 1;
    }

    pid = fork(); //first child for sort
    //printf("the pid for pipe parent is %d and the child pid is %d", getppid(), getpid());

    if (pid < 0)
    {
        fprintf(stderr, "fork error");
        return 1;
    }

    if (pid == 0)
    {
        dup2(fd1[1], 1);
        close(fd1[0]);
        //printf("the child process running sort is %d\n", getpid());
        execlp("sort", "sort", NULL);
        printf("sort exec - should not be here");
        exit(0);
    }

    wait(0);

    int fd2[2];

    if (pipe(fd2) == -1)
    {
        fprintf(stderr, "pipe failed");
        return 1;
    }

    pid = fork(); //second child for uniq

    if (pid < 0)
    {
        fprintf(stderr, "fork error\n");
        return 1;
    }

    if (pid == 0)
    {
        dup2(fd1[0], 0);
        dup2(fd2[1], 1);
        close(fd1[1]);
        close(fd2[0]);
        //printf("the child process running uniq is %d\n", pid);
        execlp("/usr/bin/uniq", "uniq", NULL);
        printf("uniq exec - you shouldnt be here");
        exit(0);
    }

    wait(0);

    pid = fork(); //3rd child process for wc

    if (pid < 0)
    {
        fprintf(stderr, "fork failed\n");
        return 1;
    }

    if (pid == 0)
    {
        dup2(fd2[0], 0);
        close(fd2[1]);
        close(fd1[0]);
        close(fd1[1]);
        //printf("the child process running wc is %d\n", getpid());
        execlp("wc", "wc", "-l", NULL);
        printf("wc exec - you shouldnt be here\n");
        exit(0);
    }
    //parent

    close(fd1[0]);
    close(fd1[1]);
    close(fd2[0]);
    close(fd2[1]);

    wait(NULL);

    printf("CHILD COMPLETE \n");
}

最佳答案

TL;DR - 父级需要close()其附加到sort输出的管道写入端的副本。在第一次等待之前添加 close(fd1[1]) 可“修复”问题。

程序在第二次调用 wait() 时“挂起”(等待 uniq 子进程退出1)。但是,uniq 永远不会退出,因为它连接到 fd1 管道读取端的标准输入永远不会关闭。该文件描述符在系统中有两个副本:第一个属于 execsort 的子进程,并且它确实按照 的预期关闭了排序。但另一个副本属于父进程,它不会 close() 它。由于管道的写入端至少还有一个打开的文件描述符,因此管道并未关闭。

此解决方案还需要将整个排序输出缓冲在管道中(即在内核中)。对于重要的输入,最好以相反的顺序 fork 子项,连接它们的所有管道,并让它们并行运行。这更接近真实、健壮的 shell 的功能。

1 或者接收信号等,健壮的 shell 应该检查这些信号。

关于C 程序模拟命令行提示符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53548243/

相关文章:

c - Double 在递归中返回 0,但在 int 时工作正常

c - 在c中使用pipe()时,我的文件描述符神秘地设置为零

bash - 在使用简单的 awk 脚本时遇到问题

c - 在C中为数组的每个元素添加前缀

c - 尝试计算使用 C 编程语言读取文件时打印的文本行数

c - 为什么这个 C 程序在函数作为参数调用时表现异常

c - c 中的 shell 未按预期工作 - 与管道损坏相关

c++ - 2个c++程序如何在同一个linux机器上调用彼此的类/函数?

c# - ffmpeg 输出管道到命名的 Windows 管道

c - 函数 usleep 的隐式声明