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

时间:2015-08-16 20:55:58

标签: c++ linux bash

我正在为练习写一个婴儿计划。我想要完成的基本上是一个简单的小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. 还有办法使用我当前的方法吗?
  3. 我已经找到了解决此问题的方法,但我找不到有关如何将值从> Bash 返回到 C ++的信息,只知道如何调用来自C ++的Bash脚本。

3 个答案:

答案 0 :(得分:8)

我们将在此处利用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

Here是关于popen的文档。快乐的编码:)

答案 1 :(得分:4)

您必须使用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;
}

答案 2 :(得分:3)

漫长的方法 - 让您完全控制子进程的stdinstdoutstderr,代价是相当复杂 - 涉及使用{{1} }和fork直接。

  1. execve之前,设置您的终端进行沟通 - fork运作良好,或pipe。我假设你已经调用过类似的东西:

    socketpair
  2. int childStdin[2], childStdout[2], childStderr[2]; pipe(childStdin); pipe(childStdout); pipe(childStderr); 之后,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) childStdinchildStdout

  3. childStderr之后,在父流程中:

    fork
  4. 现在,您的父进程可以完全控制子进程的std i / o,并且必须安全地复用 close(childStdin[0]); // parent cannot read from stdin close(childStdout[1]); // parent cannot write to stdout/stderr close(childStderr[1]); childStdin[1]childStdout[0],同时还要监视{ {1}}并最终使用childStderr[0] - 系列调用来检查进程终止代码。 SIGCLD特别适合在异步处理std i / o时处理wait。当然,请参阅pselectSIGCLD

    如果您要合并孩子的selectpoll,只需stdout并完全摆脱stderr

    手册页应填写此处的空白。如果你需要它,这就是困难的方式。