【问题标题】:How to ensure that we read all lines from boost::child process如何确保我们从 boost::child 进程中读取所有行
【发布时间】:2017-07-20 06:37:10
【问题描述】:

我在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 退出后尝试读取,管道将导致死锁

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

【问题讨论】:

  • 如果您在链接到的页面上再往下看一点,您会看到标准输入 输出的重定向。这应该告诉你如何重定向标准输出和错误。
  • 谢谢,是的,我看到了。我需要适当的 while 循环来等待 BOTH 标准输出和标准错误。也想知道循环是如何工作的。就像我们为什么不能像上面(1)中那样丢失数据。

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


【解决方案1】:

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

也许你可以简单地获得一个向量的未来,如果你真的需要它的话,你可以简单地使用 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";
    }

}

【讨论】:

    【解决方案2】:

    我也遇到了同样的问题...解决这个问题的最佳方法是使用异步 i/o。

    不幸的是,boost 文档@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
        }
    

    【讨论】:

      【解决方案3】:

      另一种解决方案。

      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);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-08-02
        • 2021-09-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-10
        • 2015-06-06
        相关资源
        最近更新 更多