【问题标题】:What interval does popen go by when running commands and how to capture output line by line?运行命令时popen的间隔是多少以及如何逐行捕获输出?
【发布时间】:2013-03-01 22:24:41
【问题描述】:

假设我在下面有这段代码,我用它来运行像ls 这样的简单命令,并假设输出是100 行长。

当我们在下面的代码中执行cout << buff 时,输出是逐字节、逐位还是逐行进行流式传输,或者间隔是多少?

假设我们要在流式传输cout<<buff 时打印每个新行的new_line! 字符串结尾。我们该怎么做?

我在想,在 while 循环中,我们应该有一些东西来检查它是否刚刚流式传输(或即将流式传输)\n 到我们的字符串流 output,并且它决定在之后(或之前)做一些事情) 它流入输出。

最初我将把它变成数据结构来存储,所以我只需要知道如何检查\n,因为我们正在缓冲,以及在哪里放置添加new_line!的函数或我想做的任何事情。

代码如下:

#include <iostream>
#include <stdio.h>

using namespace std;
// imaginary 
// stringstream output;


int main() {
    FILE *in;
    char buff[512];

    if(!(in = popen("ls -sail", "r"))){
        return 1;
    }

    while(fgets(buff, sizeof(buff), in)!=NULL){
            // output << buff;
            // if there is a new line
            // output << "new_line!";

        cout << buff;
    }
    pclose(in);

    return 0;
}

【问题讨论】:

    标签: c++ stream popen


    【解决方案1】:

    如果您使用 GNU libc,那么您可以使用 getline() 而不是 fgets。更详细的解释见here

    否则,请使用系统提供的任何工具从FILE* 中读取一行。如果你真的找不到这样的设施(这将是非常令人惊讶的),那么在最坏的情况下你总是可以使用this approachfgets 也是一个很好的解决方案,前提是您考虑了缓冲区大小限制,并在需要时自行进行更多缓冲(请查看 the doc,所有内容均已解释)。

    您必须记住的是,popen 只返回一个 FILE*,因此任何适用于标准文件的内容也适用于您的情况。网络上的例子很多。


    现在对于您问题的“间隔”部分,恐怕没有什么意义。首先,这取决于外部命令将新数据打印到标准输出的时间间隔。无论如何,只要外部命令写入它,您就可以在程序中读取它。

    【讨论】:

    • 谢谢,我不确定getline() 是否可以解决这个问题,谢谢您的澄清!
    【解决方案2】:

    管道将在数据进入时缓冲数据(直到它达到一些可能不容易找到的实现限制 - 但它是“大量” - 此时管道将在 ls 写入时“阻塞”到它,所以ls 会停止),除非你读得更快,在这种情况下你的线程会阻塞。如果您要输出到屏幕,您的输出可能会限制速率。

    这样的管道不是“块”或“行缓冲”——它只是一个管道——放在一端的任何东西都会从另一端出来。当然,如果“文件管理”的内部要求 4000 字节,则可能要等到 4000 字节在另一端通过后,才能完成读取操作。但很可能fgets 的内部结构是健全的,不会一次要求大量数据。

    【讨论】:

      【解决方案3】:

      就像fget 与 stdio。它将一直等到 EOF(程序已关闭)或缓冲区被填满。

      因此,while 循环的每次迭代都将获得 512 字节的数据,或者在程序终止或 stdio 关闭后获得剩余的数据。

      你可以用这样的小程序进行测试:

      #include <thread>
      #include <chrono>
      #include <iostream>
      
      int main() {
          std::cout << "hello" << std::endl;
          std::this_thread::sleep_for(std::chrono::seconds(5));
          std::cout << " world" << std::endl;
      }
      

      您会注意到,尽管睡眠时间很长,但它仍会尝试读取 512 个字节并最终在 stdout 读取时关闭。

      要以符合标准的方式管理实际分块,您可以使用 std::fgetc 并构建自己的缓冲区。

      http://en.cppreference.com/w/cpp/io/c/fgetc

      您自己的 getline 可能如下所示:

      std::string get_line(FILE* f) {
          char c;
          std::string out;
          while(c=std::fgetc(f)!='\n' && c!=EOF) {
               out+=c;
          }
          out+='\n';
          return out;
      }
      
      
      //...
      
      //usage
      while(!std::feof(in)) {
          cout << get_line(in);
      }
      

      【讨论】:

      • 谢谢你的例子,它真的很有帮助:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多