如何确保我们从boost :: child进程读取所有行

时间:2017-07-20 06:37:10

标签: c++ boost stdout stderr boost-process

我在boost::child文档页面上看到了以下代码,其中介绍了如何读取子进程的输出。 http://www.boost.org/doc/libs/1_64_0/doc/html/boost_process/tutorial.html 他们说在运行你的子进程后,我们可以通过这个循环来读它: -

bp::ipstream is; //reading pipe-stream
bp::child c(bp::search_patk("nm"), file, bp::std_out > is);

//then later
while (c.running() && std::getline(is, line) && !line.empty())
        data.push_back(line);

我在这里有两个问题: -

  1. 如果c.running()返回false,我们只需退出循环。在这种情况下,上面的流is可能仍会携带丢失的数据?
  2. 读取stdout和stderr的最佳方法是什么,同时确保进程exit()不会造成死锁 页面上有一条警告: - 如果您尝试读取nm退出
  3. 后,管道将导致死锁

    我希望同时捕获stdoutstderr,而不必担心nm已退出或未退出。

3 个答案:

答案 0 :(得分:3)

我认为除非你使用异步方法,否则没有正确的方法。

也许你可以简单地获得一个向量的未来,并使用string_views,如果你真的需要它逐行。

std::future<std::vector<char> > output, error;

boost::asio::io_service svc;
bp::child c(bp::search_path("nm"), file, bp::std_out > output, bp::std_err > error, svc);
svc.run();

要完全按照之前的方式阅读,可以在向量顶部使用istream:

#include <boost/process.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <iostream>

namespace bp = boost::process;
namespace bio = boost::iostreams;
std::string const file = "./a.out";

int main() {
    std::future<std::vector<char> > output, error;

    boost::asio::io_service svc;
    bp::child c(bp::search_path("nm"), file, bp::std_out > output, bp::std_err > error, svc);
    svc.run();

    //then later
    {
        auto raw = output.get();
        std::vector<std::string> data;
        std::string line;
        bio::stream_buffer<bio::array_source> sb(raw.data(), raw.size());
        std::istream is(&sb);

        while (std::getline(is, line) && !line.empty())
            data.push_back(line);

        std::cout << data.at(rand()%data.size()) << "\n";
    }

}

答案 1 :(得分:0)

我有同样的问题......解决这个问题的最佳方法是使用async i / o。

不幸的是,@ http://www.boost.org/doc/libs/master/doc/html/boost_process/extend.html#boost_process.extend.async的提升文档错了...... 它使一切看起来很简单,但没有显示缓冲区必须事先调整大小,并且掩盖了许多细节。

这是我的函数,它一次发送或接收一个缓冲区(没有像question / response0那样的交互,我使用stderr进行错误检查,因为这是我的应用程序所需要的,但你可以通过调用来捕获应用程序的退出代码“c.exit_code();`

using tstring=basic_string<TCHAR>;

void Run(
    const tstring& exeName;
    const tstring& args,
    const std::string& input,
    std::string& output,
    std::string& error
)
{
    using namespace boost;

    asio::io_service ios;

    std::vector<char> vOut(128 << 10);
    auto outBuffer{ asio::buffer(vOut) };
    process::async_pipe pipeOut(ios);

    std::function<void(const system::error_code & ec, std::size_t n)> onStdOut;
    onStdOut = [&](const system::error_code & ec, size_t n)
    {
        output.reserve(output.size() + n);
        output.insert(output.end(), vOut.begin(), vOut.begin() + n);
        if (!ec)
        {
            asio::async_read(pipeOut, outBuffer, onStdOut);
        }
    };

    std::vector<char> vErr(128 << 10);
    auto errBuffer{ asio::buffer(vErr) };
    process::async_pipe pipeErr(ios);
    std::function<void(const system::error_code & ec, std::size_t n)> onStdErr;
    onStdErr = [&](const system::error_code & ec, size_t n)
    {
        error.reserve(error.size() + n);
        error.insert(error.end(), vErr.begin(), vErr.begin() + n);
        if (!ec)
        {
            asio::async_read(pipeErr, errBuffer, onStdErr);
        }
    };

    auto inBuffer{ asio::buffer(input) };
    process::async_pipe pipeIn(ios);

    process::child c(
        exeName + _T(" ") + args, 
        process::std_out > pipeOut, 
        process::std_err > pipeErr, 
        process::std_in < pipeIn
    );


    asio::async_write(pipeIn, inBuffer, 
        [&](const system::error_code & ec, std::size_t n) 
        {
            pipeIn.async_close();
        });

    asio::async_read(pipeOut, outBuffer, onStdOut);
    asio::async_read(pipeErr, errBuffer, onStdErr);

    ios.run();
    c.wait();
}

我执行的应用程序是解码器/编码器,我发送整个文件进行处理,并以这种方式同时接收结果。没有文件大小限制。

重要

boost 1.64中有一个错误修复,只影响Windows,显然

在文件boost \ process \ detail \ windows \ async_pipe.hpp中: 参考:https://github.com/klemens-morgenstern/boost-process/issues/90

第79行:

    ~async_pipe()
    {
//fix
        //if (_sink .native()  != ::boost::detail::winapi::INVALID_HANDLE_VALUE_)
        //    ::boost::detail::winapi::CloseHandle(_sink.native());
        //if (_source.native() != ::boost::detail::winapi::INVALID_HANDLE_VALUE_)
        //    ::boost::detail::winapi::CloseHandle(_source.native());
        boost::system::error_code ec;
        close(ec);
//fix
    }

答案 2 :(得分:0)

另一种解决方案。

bp::child c(bp::search_path("nm"), file, bp::std_out > output, bp::std_err > error, svc);

此代码忽略stdout / stderr的顺序。如果订单有效,您可以这样写:

bp::child c(bp::search_path("nm"), file, (bp::std_out & bp::std_err) > outerr, svc);