带有标准输出重定向的`boost :: process`在Ubuntu 16中随机失败

Posted

技术标签:

【中文标题】带有标准输出重定向的`boost :: process`在Ubuntu 16中随机失败【英文标题】:`boost::process` with standard output redirection fails randomly with Ubuntu 16 【发布时间】:2018-03-06 21:02:04 【问题描述】:

我尝试使用boost::process,它看起来像是 boost 中非常错误的部分;无论如何,可能有人知道解决方法。

最常见的任务是执行流程并获得其完整(真正完整)的输出。通常输出也可能是二进制的,因此我们不能在一般情况下使用string

来自boost文档的异步示例不起作用,论坛上的其他文章已经提到过,所以我尝试使用最简单的同步算法。当然,我知道死锁风险,但是boost 没有走到这一步,它之前就失败了。

代码思路如下:

bool ReadPipe(boost::process::ipstream &pipe, vector<char> &output)

   char buffer[4096];
   pipe.read(buffer, 4096);
   auto bytesRead = pipe.gcount();
   if (bytesRead)
      output.insert(output.end(), buffer, buffer + bytesRead);
   return bytesRead != 0;

boost::process::ipstream output;
vector<char> processOutput;
string cmdline = "somthing";
boost::process::child c(cmdLine.c_str(),
   boost::process::std_in.close(),
   boost::process::std_out > output);
while (c.running())
   Reader::ReadPipe(output, processOutput);
Reader::ReadPipe(output, processOutput);

在这段代码中,我们创建进程,将其标准输出重定向到ipstream,在应用程序运行时读取它,并在应用程序存在后读取可能的其余数据。

在 Windows 上运行正常。在 Ubuntu 16 上它有时可以工作,有时返回部分输出,有时不返回任何输出。

有谁知道它为什么如此不稳定,有没有任何现实的方法可以使用boost::process 从任何应用程序获取完整的、可能是二进制的输出,就像 Linux 终端可以做到的那样?

【问题讨论】:

哪些命令可以提供部分输出?对于特定命令是否一致,或者同一命令有时会给出完整的输出,有时不会?我注意到您没有重定向标准错误;也许缺少的输出正在那里写。 我以同样的方式重定向stderr,我只是简化了示例。我使用adb 命令(android 调试桥)对其进行测试,例如,adb shell ifconfig 有时会返回完整输出,有时则不会。 【参考方案1】:

使用running() 会引发竞争条件。

如果程序在您消耗完所有输出之前退出,您将停止消耗。这就是你编码的明确内容。

仔细观察

使用这个人为的例子来加剧问题,以便可靠地复制它:

#include <boost/process.hpp>

namespace Reader 
    static constexpr size_t buf_size = 4096;

    bool ReadPipe(boost::process::ipstream &pipe, std::vector<char> &output)
    
        std::this_thread::sleep_for(std::chrono::milliseconds(500));

        char buffer[buf_size];
        pipe.read(buffer, sizeof(buffer));
        auto bytesRead = pipe.gcount();
        if (bytesRead)
            output.insert(output.end(), buffer, buffer + bytesRead);

        return bytesRead != 0;
    


#include <iostream>

int main() 
    boost::process::ipstream output;
    std::vector<char> processOutput;
    std::string cmdline = "/bin/bash";
    boost::process::child c(cmdline.c_str(), std::vector<std::string>  "-c", "(dd if=/dev/urandom bs=1024 count=10 | xxd); echo -e '\\nComplete, bye!'" ,
            boost::process::std_in.close(),
            boost::process::std_out > output);

    while (c.running())
        Reader::ReadPipe(output, processOutput);

    std::cout.write(processOutput.data(), processOutput.size()) << std::endl;

将输出通过|tail 打印出类似

如您所见,它确实是不完整的。减小 dd 大小,例如

boost::process::child c(cmdline.c_str(), std::vector<std::string>  "-c", "(dd if=/dev/urandom bs=32 count=10 | xxd); echo -e '\\nComplete, bye!'" ,

显示预期

路上有一只熊

有一个丑陋的问题:tutorial example 有以下不祥的警告:

因此,实际上在进程退出后是否可以安全地清空输入缓冲区并不完全清楚。我的直觉告诉我这实际上很好,但更重要的是,事情可以简单得多:

幸运的是,我们可以飞(而熊不能)

#include <boost/process.hpp>
#include <boost/asio.hpp>
#include <iostream>

int main() 
    std::future<std::vector<char>> processOutput;
    std::string cmdline = "/bin/bash";
    boost::asio::io_service io;
    boost::process::child c(cmdline.c_str(), std::vector<std::string>  "-c", "(dd if=/dev/urandom bs=1024 count=10 | xxd); echo -e '\\nComplete, bye!'" ,
            boost::process::std_in.close(),
            boost::process::std_out > processOutput,
            io);

    io.run();
    c.wait();

    auto output = processOutput.get();
    std::cout.write(output.data(), output.size()) << std::endl;

这在没有竞争条件的情况下按预期工作。对于更复杂的用法(例如与输入动态确定输出的子进程的“实时”对话),请考虑使用异步接口,例如boost process running() and exit_code() thread safety 或 Boost::process output blank lines

【讨论】:

如果子进程输出大于管道缓冲区的大小,这不会也死锁吗? @DanielTrebbien 如果您使用可能发生的固定大小的缓冲区。但是,我的示例都没有使用固定大小的缓冲区。 (具有讽刺意味的是,我错过了您的评论,现在才看到它,因为我在这里的最后一个示例中spotted a bug lurking)

以上是关于带有标准输出重定向的`boost :: process`在Ubuntu 16中随机失败的主要内容,如果未能解决你的问题,请参考以下文章

LLDB 重定向劣质标准输出

Linux 重定向与管道符

重定向进程输出[关闭]

怎样去掉提示:nohup:重定向标准错误到标准输出

重定向标准错误到标准输出 是啥意思

LINUX 标准错误输出重定向