首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在C的`execv`中组合`lshw`和`grep`命令?

如何在C的`execv`中组合`lshw`和`grep`命令?
EN

Stack Overflow用户
提问于 2020-04-30 02:12:38
回答 1查看 70关注 0票数 0

下面是一个C程序,它通过连续调用lshw (以访问具有相应属性的总硬件列表)和grep (仅从lshw结果中选择相关点)来查找特定属性,如CPU总线信息:

代码语言:javascript
复制
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。是什么使得这个程序不能只显示重要的信息,以及我必须用什么方式来设置管道?

EN

回答 1

Stack Overflow用户

发布于 2020-05-05 01:12:49

竖线不是用来分隔命令的参数,因为execve(2)系统调用只会将程序加载到one process的虚拟空间中。您需要创建两个进程,一个是您想要执行的命令,一个是您想要执行的命令,并对它们进行通信,以便从一个进程的输入转到另一个进程的输出。我认为您还会对最后一个命令的输出感兴趣,因此需要执行两个重定向(一个从第一个命令重定向到第二个命令,另一个重定向从第二个命令的输出重定向到管道描述符)、两个fork和两个exec。

首先,好消息是,您可以通过对popen(3)的简单调用来完成所有这些工作,而不需要在从单个命令重定向i/o时创建分支和execs。只需使用以下命令:

代码语言:javascript
复制
#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就行了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61508772

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档