从bash脚本返回输出到调用c++函数

Returning output from bash script to calling C++ function

本文关键字:调用 c++ 函数 输出 bash 脚本 返回      更新时间:2023-10-16

我正在写一个婴儿程序练习。我试图完成的基本上是一个简单的小GUI显示服务(Linux);有启动、停止、启用和禁用服务的按钮(很像Windows中的msconfig应用程序"服务"选项卡)。我在Fedora 21上使用c++和Qt Creator。

我想用c++创建GUI,并通过调用bash脚本来用服务列表填充GUI,并在单击按钮时调用bash脚本来执行适当的操作(启用,禁用等)

但是当c++ GUI调用bash脚本(使用system("path/to/script.sh"))时,返回值仅用于退出成功。我如何接收脚本本身的输出,以便我可以反过来使用它在GUI上显示?

举个概念性的例子:如果我想把(systemctl --type service | cut -d " " -f 1)的输出显示到我用c++创建的GUI中,我该怎么做呢?这是我想要完成的事情的正确方法吗?如果没有,

  1. 什么是正确的方式?和
  2. 是否还有一种方法可以使用我当前的方法?

我已经找到了解决这个问题的方法,但我找不到关于如何从 Bash c++返回值的信息,只有如何从c++调用 Bash脚本。

我们将在这里利用popen函数。

std::string exec(char* cmd) {
    FILE* pipe = popen(cmd, "r");
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while(!feof(pipe)) {
        if(fgets(buffer, 128, pipe) != NULL)
            result += buffer;
    }
    pclose(pipe);
    return result;
}

该函数接受命令作为参数,并以string的形式返回输出。

注意:这将捕获stderr!一个快速而简单的解决方法是 stderr重定向到stdout,在命令末尾使用2>&1

这是popen的文档。快乐编码:)

您必须使用popen而不是system来运行命令,然后循环遍历返回的文件指针。

下面是ls -l 命令的一个简单示例
#include <stdio.h>
#include <stdlib.h>
int main() {
    FILE *process;
    char buff[1024];
    process = popen("ls -l", "r");
    if (process != NULL) {
        while (!feof(process)) {
            fgets(buff, sizeof(buff), process);
            printf("%s", buff);
        }
        pclose(process);
    }
    return 0;
}

长方法——它让您完全控制子进程的stdinstdoutstderr,代价是相当大的复杂性——包括直接使用forkexecve

  1. fork之前,设置您的通信端点- pipe工作良好,或socketpair。我假设您调用了如下内容:

    int childStdin[2], childStdout[2], childStderr[2];
    pipe(childStdin);
    pipe(childStdout);
    pipe(childStderr);
    
  2. fork之后,execve之前的子进程:

    dup2(childStdin[0], 0);  // childStdin read end to fd 0 (stdin)
    dup2(childStdout[1], 1); // childStdout write end to fd 1 (stdout)
    dup2(childStderr[1], 2); // childStderr write end to fd 2 (stderr)
    

    . .然后关闭childStdinchildStdoutchildStderr的所有

  3. fork之后,父进程:

     close(childStdin[0]);  // parent cannot read from stdin
     close(childStdout[1]); // parent cannot write to stdout/stderr
     close(childStderr[1]);
    

现在,父进程完全控制了子进程的std i/o,并且必须安全地复用childStdin[1]childStdout[0]childStderr[0],同时还监视SIGCLD,并最终使用wait系列调用来检查进程终止代码。pselect特别适合在异步处理std i/o时处理SIGCLD。当然也可以参见selectpoll

如果你想合并孩子的stdoutstderr,只dup2(childStdout[1], 2),把childStderr完全去掉。

手册页应该填补这里的空白。如果你需要的话,这是比较困难的方法