如何确保我们读取了 boost::child 进程中的所有行
How to ensure that we read all lines from boost::child 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);
我有 2 个问题:-
- 如果
c.running()
returns为假,我们就直接退出循环。在那种情况下,上面的流 is
可能仍然携带丢失的数据?
- 同时读取 stdout 和 stderr 的最佳方法是什么,同时确保进程 exit() 不会造成死锁
页面上有一条警告:-
如果在nm退出后尝试读取管道会导致死锁
我希望同时捕获 stdout
和 stderr
而不必担心 nm
是否已退出。
我认为除非使用异步方法,否则没有正确的方法。
也许你可以简单地得到一个向量的 future 并使用 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";
}
}
我遇到了同样的问题...处理此问题的最佳方法是使用异步 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();
}
我执行的应用程序是 decoder/encoder,我发送整个文件进行处理,并通过这种方式同时接收结果。没有文件大小限制。
重要
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
}
另一个解决方案。
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);
我在 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);
我有 2 个问题:-
- 如果
c.running()
returns为假,我们就直接退出循环。在那种情况下,上面的流is
可能仍然携带丢失的数据? - 同时读取 stdout 和 stderr 的最佳方法是什么,同时确保进程 exit() 不会造成死锁
页面上有一条警告:-
如果在nm退出后尝试读取管道会导致死锁
我希望同时捕获 stdout
和 stderr
而不必担心 nm
是否已退出。
我认为除非使用异步方法,否则没有正确的方法。
也许你可以简单地得到一个向量的 future 并使用 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";
}
}
我遇到了同样的问题...处理此问题的最佳方法是使用异步 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();
}
我执行的应用程序是 decoder/encoder,我发送整个文件进行处理,并通过这种方式同时接收结果。没有文件大小限制。
重要
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
}
另一个解决方案。
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);