我正在寻找一种方法来获得命令的输出,当它从c++程序中运行时。我已经看到了使用system()函数,但它只会执行一个命令。以下是我正在寻找的一个例子:

std::string result = system("./some_command");

我需要运行任意命令并获得其输出。我在boost.org上找过了,但是我还没有找到任何能满足我需要的东西。


当前回答

两种可能的方法:

我不认为popen()是c++标准的一部分(从内存来看,它是POSIX的一部分),但它在我使用过的每个UNIX上都可用(您似乎针对的是UNIX,因为您的命令是./some_command)。 在不存在popen()的情况下,您可以使用system("。/some_command >/tmp/some_command.out");,然后使用正常的I/O函数处理输出文件。

其他回答

两种可能的方法:

我不认为popen()是c++标准的一部分(从内存来看,它是POSIX的一部分),但它在我使用过的每个UNIX上都可用(您似乎针对的是UNIX,因为您的命令是./some_command)。 在不存在popen()的情况下,您可以使用system("。/some_command >/tmp/some_command.out");,然后使用正常的I/O函数处理输出文件。

#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
    if (!pipe) {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

Pre-C + + 11版:

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>

std::string exec(const char* cmd) {
    char buffer[128];
    std::string result = "";
    FILE* pipe = popen(cmd, "r");
    if (!pipe) throw std::runtime_error("popen() failed!");
    try {
        while (fgets(buffer, sizeof buffer, pipe) != NULL) {
            result += buffer;
        }
    } catch (...) {
        pclose(pipe);
        throw;
    }
    pclose(pipe);
    return result;
}

对于Windows,用_popen和_pclose替换popen和pclose。

下面可能是一个可移植的解决方案。它遵循标准。

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <sstream>

std::string ssystem (const char *command) {
    char tmpname [L_tmpnam];
    std::tmpnam ( tmpname );
    std::string scommand = command;
    std::string cmd = scommand + " >> " + tmpname;
    std::system(cmd.c_str());
    std::ifstream file(tmpname, std::ios::in | std::ios::binary );
    std::string result;
    if (file) {
        while (!file.eof()) result.push_back(file.get())
            ;
        file.close();
    }
    remove(tmpname);
    return result;
}

// For Cygwin

int main(int argc, char *argv[])
{
    std::string bash = "FILETWO=/cygdrive/c/*\nfor f in $FILETWO\ndo\necho \"$f\"\ndone ";
    std::string in;
    std::string s = ssystem(bash.c_str());
    std::istringstream iss(s);
    std::string line;
    while (std::getline(iss, line))
    {
        std::cout << "LINE-> " + line + "  length: " << line.length() << std::endl;
    }
    std::cin >> in;
    return 0;
}

命令类使用system(“cmd > stdout 2> stderr”)为用户提供标准输出和标准输出,以及退出码。

测试运行:

./a.out 'ls .'
exit code: 0
stdout: HelloWorld
HelloWorld.c
HelloWorld.cpp
HelloWorld.dSYM
a.out
gcc_container.bash
linuxsys
macsys
test.sh

stderr: 

#include <iostream>
#include <fstream>
#include <sstream>
#include <unistd.h>
using namespace std;

class Command {
    public:
        Command() {
            exit_code_ = -1;
        }

        int GetExitCode() { return exit_code_;}

        string GetStdOutStr() {return stdout_str_;}

        string GetStdErrStr() {return stderr_str_;}

        int Run(const char* cmd) {
            return Run(string(cmd));
        }

        /**
         * @brief run a given command
         * 
         * @param cmd: command string
         * @return int: the exit code of running the command
         */
        int Run(string cmd) {

            // create temp files
            char tmp_dir[] = "/tmp/stdir.XXXXXX";
            mkdtemp(tmp_dir);
            string stdout_file = string(tmp_dir) + "/stdout";
            string stderr_file = string(tmp_dir) + "/stderr";

            // execute the command "cmd > stdout_file 2> stderr_file"
            string cli = cmd + " > " + stdout_file + " 2> " + stderr_file;
            exit_code_ = system(cli.c_str());
            exit_code_ = WEXITSTATUS(exit_code_);
            stdout_str_ = File2Str(stdout_file);
            stderr_str_ = File2Str(stderr_file);

            // rid of the temp files
            remove(stdout_file.c_str());
            remove(stderr_file.c_str());
            remove(tmp_dir);

            return exit_code_;
        }

    private:
        int exit_code_;
        string stderr_str_;
        string stdout_str_;

        /**
         * @brief read a file
         * 
         * @param file_name: file path 
         * @return string the contents of the file.
         */
        string File2Str(string file_name) {
            ifstream file;
            stringstream str_stream;

            file.open(file_name);
            if (file.is_open()) {
                str_stream << file.rdbuf();
                file.close();
            }
            return str_stream.str();
        }
};

int main(int argc, const char* argv[]) {
    Command command;

    command.Run(argv[1]);
    cout << "exit code: " << command.GetExitCode() << endl;
    cout << "stdout: " << command.GetStdOutStr() << endl;
    cout << "stderr: " << command.GetStdErrStr() << endl;
    return  command.GetExitCode();
}

waqas的答案的c++流实现:

#include <istream>
#include <streambuf>
#include <cstdio>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <string>

class execbuf : public std::streambuf {
    protected:
        std::string output;
        int_type underflow(int_type character) {
            if (gptr() < egptr()) return traits_type::to_int_type(*gptr());
            return traits_type::eof();
        }
    public:
        execbuf(const char* command) {
            std::array<char, 128> buffer;
            std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command, "r"), pclose);
            if (!pipe) {
                throw std::runtime_error("popen() failed!");
            }
            while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
                this->output += buffer.data();
            }
            setg((char*)this->output.data(), (char*)this->output.data(), (char*)(this->output.data() + this->output.size()));
        }
};

class exec : public std::istream {
    protected:
        execbuf buffer;
    public:
        exec(char* command) : std::istream(nullptr), buffer(command, fd) {
            this->rdbuf(&buffer);
        }
};

这段代码通过stdout捕获所有输出。如果你只想捕获stderr,那么像这样传递你的命令:

sh -c '<your-command>' 2>&1 > /dev/null

如果你想同时捕获stdout和stderr,那么命令应该是这样的:

sh -c '<your-command>' 2>&1