Как убедиться, что мы читаем все строки из процесса boost :: child

Я видел следующий код на 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 вопроса здесь: —

  1. Если c.running() возвращает false, мы просто выходим из цикла. И в этом случае поток is выше может все еще нести данные, которые теряются?
  2. Как лучше всего читать stdout и stderr оба, при этом убедившись, что процесс exit () не создает тупик
    На странице есть предупреждение:
    Канал вызовет тупик, если вы попытаетесь прочитать после выхода из NM

Я хочу захватить оба stdout а также stderr не беспокоясь о nm вышел или нет выше.

7

Решение

Я думаю, что нет правильного способа, если вы не используете асинхронные методы.

Возможно, вы можете просто получить будущее для вектора и использовать 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";
}

}
3

Другие решения

У меня была та же проблема … Лучший способ справиться с этим — использовать асинхронный ввод-вывод.

К сожалению, буст документации http://www.boost.org/doc/libs/master/doc/html/boost_process/extend.html#boost_process.extend.async был неправ…
Это делает все это простым, но не показывает, что размер буферов должен быть заранее определен, и затушевывает многие детали.

Вот моя функция, которая отправляет или получает один буфер за один раз (никакого взаимодействия, такого как вопрос / 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();
}

Приложение, которое я запускаю, является декодером / кодировщиком, я отправляю весь файл для обработки и таким образом получаю результаты одновременно. Нет ограничений на размер файла.

ВАЖНЫЙ

В бусте 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
}
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);
0