如何在C语言中创建一个Linux管道示例

6

我正在尝试学习如何使用C语言中的pipe()命令,并尝试创建一个测试程序来复制在Linux终端输入ls | grep ".c"时的功能。如果我在终端中输入这个命令,我只会得到test.c作为结果。

我的代码如下:

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"

int main(int argc, char** argv)
{
 int pipefd[2];
 int childpid,childpid2;
 char* cmd[3]={"ls",NULL,NULL};
 char* cmd2[3]={"grep",".c",NULL};
 pipe(pipefd);
 if(childpid=fork()){
   //parent
 }else{  
   //child
   //write
   close(pipefd[0]);
   dup2(pipefd[1],STDOUT_FILENO);
   execvp("ls", cmd);
 }
 if(childpid2=fork()){
 }
 else{
   close(pipefd[1]);
   dup2(pipefd[0],STDIN_FILENO);
   execvp("grep",cmd2);
 }
 close(pipefd[0]);
 close(pipefd[1]);
 return 0;
}

这段代码会返回以下结果($是终端提示符):
$a.out
$test.c
(blank line)

程序没有完成,但是一直挂起直到我退出。我有什么问题?如何模拟终端?我是C语言新手,正在使用一个预先制作的程序模板,如果有明显的错误,请原谅。


除了 test.c 以外,你的目录中还有哪些文件 - 你是否期望输出多行? - Floris
我只期望找到test.c文件。我只是试图在目录中找到C文件,但它没有给我另一个终端提示符。 - ngwilliams
4个回答

5

试试这个:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char** argv)
{
 int pipefd[2];
 int childpid,childpid2;
 char* cmd[3]={"ls",NULL,NULL};
 char* cmd2[3]={"grep",".c",NULL};
 pipe(pipefd);
 if(childpid=fork()){
   //parent
   close(pipefd[1]);
   dup2(pipefd[0],STDIN_FILENO);
   execvp("grep",cmd2);
 }else{  
   //child
   //write
   close(pipefd[0]);
   dup2(pipefd[1],STDOUT_FILENO);
   execvp("ls", cmd);
 }
 return 0;
}

3

实际上,程序会立即退出-事实上,在子进程运行之前,父进程就已经退出了,这就是为什么在“test.c”之前有一个shell提示符。

您可以通过在父进程中添加以下内容来改善情况:

wait(childpid);
wait(childpid2);

这将使父进程在两个子进程结束后退出。


1
Your program immediately terminates, while your processes continue running in the background. They overwrite the prompt, making you believe that the program is still running, even though the shell is waiting for your input (press enter or blindly type a command to see).
You only see "test.c" because it is the only file in your directory that matches the search criteria (also note that you are searching for "filenames containing c anywhere except the first character," not "ending in .c," which would be "grep '\.c$'").
The solution is simple: add the following line:
wait(NULL); wait(NULL);

在你的return 0之前。

1
.c 结尾的文件如果您想要精确匹配,需要使用 grep "\.c$",因为单独的点代表 "任意字符"。更常用的方式是 ls *.c - 但这并不能展示使用管道符的用法... - Floris
2
标记吃掉了 \。但是在这里不能使用 ls *.c,因为通配符是一个 shell 特性。 - that other guy

0

这个问题有点老了,但是这里有一个以前没有提供的旧答案。使用libpipeline。libpipeline是一个管道操作库。使用案例是man页面维护者经常需要使用类似以下命令(并解决相关操作系统错误):

zsoelim < input-file | tbl | nroff -mandoc -Tutf8

这是libpipeline的方法:
pipeline *p;
int status;

p = pipeline_new ();
pipeline_want_infile (p, "input-file");
pipeline_command_args (p, "zsoelim", NULL);
pipeline_command_args (p, "tbl", NULL);
pipeline_command_args (p, "nroff", "-mandoc", "-Tutf8", NULL);
status = pipeline_run (p);

libpipeline 的主页上有更多的例子。该库也被包含在许多发行版中,包括 Arch、Debian、Fedora、Linux from Scratch 和 Ubuntu。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接