使用Unix管道读取infile并对其进行排序

时间:2016-12-20 06:14:02

标签: c unix pipeline

我有一个练习,我应该读取一个infile文本并用Unix管道(fork()等)传递它们并将它们打印到屏幕上以供初学者使用。我已经完成了但是现在我需要对来自在管道内输入(exec sort),然后打印出来。

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

int main(void)
{

FILE *readchar = fopen("text1", "r");
    char ch;
    int fd[2];
    int i = 0;
    char readbuffer[1024];
    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe");
        exit(-1);
    }

if (fork() == 0)
{
printf("childprocess\n");

 while(1)
{     
ch = fgetc(readchar);
if(ch==EOF){
   break;
}
write(fd[1],&ch,1); 
dup2(1,fd[1]);
execlp("sort", "sort",  (char*) NULL); //this is crashing 
printf("%c",ch);
}


printf("\n");
exit(0);
printf("end of childprocess\n");
}else
  {
  wait(0);
  printf("%d: parentprocess\n", (int)getpid());
  read(fd[0],readbuffer,sizeof(readbuffer));

  printf("that was in the pipe : \n");
  printf ("%s",readbuffer);
  printf("\n");      


  }

    return 0;
}

我可以在while(1)循环中使用execlp排序吗?我的意思是每当一个角色进入管道时,exec就会对管道进行分类。 或者我可以在while(1)语句之后(在管道中添加所有字符之后)对整个管道进行排序?当execlp排序开始时,代码现在只在第一个循环中崩溃。

infile数据:

abcdefg
123456
XXXXXX
01010101

1 个答案:

答案 0 :(得分:2)

你拨打pipe()两次;删除第一个。你打电话给fork()两次;它不清楚你要删除哪一个。但是,删除第一个意味着最少的其他更改。您还需要避免关闭父级中的fd[1],以便第二个和后续子级仍具有要使用的有效管道。

代码略有缺陷

当输出直接发送到终端(而不是通过管道)时,此代码至少有效:

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

int main(void)
{
    FILE *readchar = fopen("infile", "r");
    char buf[1024];
    int fd[2];
    int i = 0;

    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe");
        exit(-1);
    }

    while (fgets(buf, sizeof(buf), readchar) != NULL)
    {
        if (fork() == 0)
        {
            printf("child proceess\n");
            close(fd[0]);
            write(fd[1], buf, sizeof(buf));
            exit(0);
        }
        else
        {
            wait(0);
            printf("parent proceess");
            //close(fd[1]);
            read(fd[0], buf, 1024);
            printf("buf: %s\n", buf);
            printf("%d\n", ++i);
        }
    }

    return 0;
}

如果将源副本复制到infile,则会产生正确的输出行数(双倍行距)。

代码仍然不是很好,原因很多,其中最重要的是每行一个进程&#39;有点浪费,并且用缓冲区中的行内容分叉进程,然后将该缓冲区写回到父进程(已经知道缓冲区中的内容)是有点无意义的#39;。 OTOH,它确实使用管道在两个进程之间进行通信。

需要更多工作,但没有无限循环

当我运行数据文件中显示的代码时:

abcdefg
123456
XXXXXX
01010101

我得到的输出是:

child proceess
parent proceessbuf: abcdefg

1
child proceess
parent proceessbuf: abcdefg

1
parent proceessbuf: 123456

2
child proceess
parent proceessbuf: abcdefg

1
parent proceessbuf: 123456

2
parent proceessbuf: XXXXXX

3
child proceess
parent proceessbuf: abcdefg

1
parent proceessbuf: 123456

2
parent proceessbuf: XXXXXX

3
parent proceessbuf: 01010101

4
嗯......这有点奇怪:第一个;那么1,2;那么1,2,3;然后是1,2,3,4。(在我的试验中,我没有发现这一点,最后的43行蜿蜒过去了两倍。)让我调查一下。但是没有无限循环,因此您将我的代码中的错误内容合并到您的内容中。

管道输出将线缓冲更改为完全缓冲

我为程序使用了名称xc19(来源xc19.c)。

上面代码的问题是我使用xc19 | pbcopy来运行并将输出复制到剪贴板(在Mac上)。这意味着输出不再是行缓冲,而是完全缓冲的#39;因此,中间输出仍然在每个子节点的标准I / O缓冲区中,因此当子进程退出时,该信息被刷新。但每个孩子都在缓冲区中获得了更多信息。

修复非常简单:在父代码中使用fflush(0);fflush(stdout);。这是一个具有更多压缩输出的版本(以及打印的PID形式的诊断):

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

int main(void)
{
    FILE *readchar = fopen("infile", "r");
    char buf[1024];
    int fd[2];
    int i = 0;

    int ret = pipe(fd);
    if (ret == -1)
    {
        perror("pipe");
        exit(-1);
    }

    while (fgets(buf, sizeof(buf), readchar) != NULL)
    {
        if (fork() == 0)
        {
            printf("%d: child proceess\n", (int)getpid());
            close(fd[0]);
            write(fd[1], buf, sizeof(buf));
            exit(0);
        }
        else
        {
            wait(0);
            printf("%d: parent proceess\n", (int)getpid());
            read(fd[0], buf, 1024);
            printf("%d buf: %s", ++i, buf);
            fflush(0);
        }
    }

    return 0;
}

输出:

58878: child proceess
58876: parent proceess
1 buf: abcdefg
58879: child proceess
58876: parent proceess
2 buf: 123456
58880: child proceess
58876: parent proceess
3 buf: XXXXXX
58881: child proceess
58876: parent proceess
4 buf: 01010101