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

Read infile with Unix pipe and sort it

我有一个练习,我应该读取一个 infile 文本并使用 Unix 管道(fork() 等)传递它们并将它们打印到屏幕上,因为 starters.I' 已经完成了但现在我需要排序管道中 infile 中的字符(exec 排序),然后将其打印出来。

#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

您调用 pipe() 两次;删除第一个。您调用 fork() 两次;不清楚应该删除哪一个。但是,删除第一个意味着最少的其他更改。您还需要避免在 parent 中关闭 fd[1],以便第二个和随后的 children 仍然可以使用有效的管道。

略有缺陷的代码

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

#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,它产生了正确的输出行数(双倍行距)。

代码仍然不好,原因有很多,其中最重要的是 'one process per line' 有点浪费,还有 'fork a process with the content of the line in a buffer, then write that buffer back to the parent (which already knew what was in the buffer) is a bit pointless'。 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 上)。这意味着输出不再是行缓冲的,而是 'fully buffered'。因此,中间输出仍在每个 child 的标准 I/O 缓冲区中,因此当 child 进程退出时,该信息被刷新。但是每个 child 都在缓冲区中获得了更多信息。

修复非常简单:在 parent 代码中使用 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