下面是一个C程序,它通过连续调用lshw (以访问具有相应属性的总硬件列表)和grep (仅从lshw结果中选择相关点)来查找特定属性,如CPU总线信息:
char *strCombine(char *str1, char *str2, int n)
{
int i = strlen(str2);
int j = 0;
if((str2 = (char *) realloc(str2, (i + n + 1))) == NULL)
perror(0);
while(j < n && str1[j])
{
str2[i] = str1[j];
i++;
j++;
}
str2[i] = 0;
return (str2);
}
int main()
{
pid_t parent;
char buf[1000] = {0};
char *str;
char *argv[6] = {"/usr/bin/lshw", "-C", "CPU", "|", "grep", "bus info"};
int fd[2];
int ret;
if(pipe(fd) == -1)
{
perror(NULL);
return -1;
}
parent = fork();
if(parent == 0)
{
close(fd[1]);
while((ret = read(fd[0], buf, 1000)))
str = strCombine(buf, str, ret);
close(fd[0]);
}
else
{
close(fd[0]);
execv(argv[0], argv);
close(fd[1]);
wait(0);
}
wait(0);
printf("%s", str);
return 0;
}在这段代码中,grep应该遵循lshw,因为两者都是通过调用execv来执行的。然而,这个管道无法工作,因为lshw用法参考在终端(运行在Ubuntu18.04LTS上)中打印出来,而不是最初需要的bus info。是什么使得这个程序不能只显示重要的信息,以及我必须用什么方式来设置管道?
发布于 2020-05-05 01:12:49
竖线不是用来分隔命令的参数,因为execve(2)系统调用只会将程序加载到one process的虚拟空间中。您需要创建两个进程,一个是您想要执行的命令,一个是您想要执行的命令,并对它们进行通信,以便从一个进程的输入转到另一个进程的输出。我认为您还会对最后一个命令的输出感兴趣,因此需要执行两个重定向(一个从第一个命令重定向到第二个命令,另一个重定向从第二个命令的输出重定向到管道描述符)、两个fork和两个exec。
首先,好消息是,您可以通过对popen(3)的简单调用来完成所有这些工作,而不需要在从单个命令重定向i/o时创建分支和execs。只需使用以下命令:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *cmd = "/usr/bin/lshw -C CPU | grep 'bus info'";
int n = 0;
char line[1000];
/* f will be associated to the output of the pipeline, so you can read from it.
* this is stated by the "r" of the second parameter */
FILE *f = popen(cmd, "r");
if (!f) {
perror(cmd);
exit(EXIT_FAILURE);
}
/* I read, line by line, and process the output,
* printing each line with some format string, but
* you are free here. */
while (fgets(line, sizeof line, f)) {
char *l = strtok(line, "\n");
if (!l) continue;
printf("line %d: [%s]\n", ++n, l);
}
/* once finished, you need to pclose(3) it. This
* makes program to wait(2) for child to finish and
* closing descriptor */
pclose(f);
}如果您需要挂载这样的管道,您将不再需要从第一个命令重定向到第二个,从第二个重定向到父进程,并且fork/exec这两个进程都要自己执行。
在这种方法中,您处理一个子subshell来为您执行管道和重定向工作,并且您只需要获得一个FILE *描述符就可以读取。
(如果我有时间,我会向您展示N个命令链的完整示例,这些命令带有重定向,但我不能保证,因为我必须编写代码)
备注
fork()返回子进程的pid给父进程,0返回子进程本身。我不明白为什么要有一个名为parent的变量来存储从fork()接收到的值。如果它为非零(非负),则表示子进程的pid。您需要两个进程,因为您需要两个进程。在我发布的示例中,您创建了三个进程(您要求一个子subshell为您挂载管道,因此您有一个子subshell,您指示它创建另外两个进程,以执行您的命令)。如果您必须挂载所有这些工具,您还需要对子进程执行wait(2) (这是在pclose(3)调用中完成的)
我有一个小程序,可以重复产生一个进程(只有一个),同时在相同的位置打印它的输出。当我尝试查看ls -l的输出(显示正在填充的文件的增长)或df命令的输出时,我会将其用作某种htop程序。它启动程序,生成一个分支,将其输出重定向到管道,并获取命令的输出以计算输出的行数(发出转义序列以将光标放在清单的顶部,并在每一个输出行之后发出一个到行尾的清除标记,以便较短的行不会被较长的行模糊。它向您展示了如何处理forks和exec系统调用,并且您可以使用示例来说明如何以勇敢的方式完成这些工作。但我认为拥有popen(3)是解决问题的办法。如果你想看看我的cont程序,只要找到here就行了。
https://stackoverflow.com/questions/61508772
复制相似问题