首页 > 解决方案 > 模拟命令行提示符的 C 程序

问题描述

我正在尝试实现一个类似于 Linux shell 命令提示符的小型 C 程序$ 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");
}

标签: cpipe

解决方案


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

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

该解决方案还要求将整个排序输出缓冲在管道中(即在内核中)。对于非平凡的输入,最好以相反的顺序分叉子节点,连接它们的所有管道,并让它们并行运行。这更接近于真正的、健壮的外壳所能做的。

1或接收信号等,稳健的外壳应检查该信号。


推荐阅读