代码之家  ›  专栏  ›  技术社区  ›  Bill the Lizard

如何读取C++中系统调用的结果?

  •  1
  • Bill the Lizard  · 技术社区  · 16 年前

    我正在使用以下代码尝试读取 df Linux中的命令使用 popen .

    #include <iostream> // file and std I/O functions
    
    int main(int argc, char** argv) {
        FILE* fp;
        char * buffer;
        long bufSize;
        size_t ret_code;
    
        fp = popen("df", "r");
        if(fp == NULL) { // head off errors reading the results
            std::cerr << "Could not execute command: df" << std::endl;
            exit(1);
        }
    
        // get the size of the results
        fseek(fp, 0, SEEK_END);
        bufSize = ftell(fp);
        rewind(fp);
    
        // allocate the memory to contain the results
        buffer = (char*)malloc( sizeof(char) * bufSize );
        if(buffer == NULL) {
            std::cerr << "Memory error." << std::endl;
            exit(2);
        }
    
        // read the results into the buffer
        ret_code = fread(buffer, 1, sizeof(buffer), fp);
        if(ret_code != bufSize) {
            std::cerr << "Error reading output." << std::endl;
            exit(3);
        }
    
        // print the results
        std::cout << buffer << std::endl;
    
        // clean up
        pclose(fp);
        free(buffer);
        return (EXIT_SUCCESS);
    }
    

    这个代码给了我一个“内存错误”,退出状态为“2”,所以我可以看到 哪里 失败了,我只是不明白 为什么? .

    我把我在上面找到的示例代码放在一起 Ubuntu Forums C++ Reference 所以我没有和它结婚。如果有人能提出一种更好的方法来读取system()调用的结果,我会接受新的想法。

    编辑为原始: 可以, bufSize 现在我明白为什么了。你不能像我天真地想做的那样随意地进入管道。

    我不是第一个尝试这样做的人。有人能给出(或指向我)一个如何读取C++中变量的系统调用结果的例子吗?

    9 回复  |  直到 11 年前
        1
  •  5
  •   CesarB    11 年前

    为什么会 std::malloc() 失败了?

    显而易见的原因是“因为 std::ftell() 返回一个负的有符号数,然后将其视为一个巨大的无符号数”。

    根据 the documentation , STD::fTele:() 失败时返回-1。它失败的一个明显原因是 不能在管道或FIFO中查找 .

    没有转义;如果不读取命令输出,就无法知道其长度,并且只能读取一次。您必须分块读取它,或者根据需要增加缓冲区,或者动态解析。

    但是,当然,您可以直接使用系统调用来避免整个问题。 df 可能用于获取信息: statvfs() .

        2
  •  6
  •   Charlie Martin    16 年前

    你做得太难了。 波彭(3) 返回常规的旧 FILE * 对于标准管道文件,也就是说,换行终止记录。您可以使用 FGET(3) 像C一样:

    #include <stdio.h>
    char bfr[BUFSIZ] ;
    FILE * fp;
    // ...
    if((fp=popen("/bin/df", "r")) ==NULL) {
       // error processing and return
    }
    // ...
    while(fgets(bfr,BUFSIZ,fp) != NULL){
       // process a line
    }
    

    在C++中,它更容易——

    #include <cstdio>
    #include <iostream>
    #include <string>
    
    FILE * fp ;
    
    if((fp= popen("/bin/df","r")) == NULL) {
        // error processing and exit
    }
    
    ifstream ins(fileno(fp)); // ifstream ctor using a file descriptor
    
    string s;
    while (! ins.eof()){
        getline(ins,s);
        // do something
    }
    

    这里还有一些错误处理,但这就是主意。重点是你对待 文件* 波彭 就像 任何 文件* 并逐行阅读。

        3
  •  3
  •   arul    16 年前

    我不确定你能不能像这样浏览/浏览所有的管道流。

    你检查过bufsize的值吗?malloc失败的一个原因是缓冲区大小异常。

        4
  •  3
  •   Josh Kelley    16 年前

    (术语注释:在Unix和Linux中,“系统调用”通常指从用户空间代码调用内核函数。将其称为“ system() 调用“或”的结果 system(3) 调用“会更清楚,但最好只说“捕获进程的输出”。)

    无论如何,您可以像读取任何其他文件一样读取进程的输出。明确地:

    • 您可以使用 pipe() , fork() exec() . 这为您提供了一个文件描述符,然后您可以使用循环 read() 从文件描述符到缓冲区 close() 文件描述符。这是最低级别的选项,并且给您最大的控制权。
    • 您可以使用 popen() 和你一样。这将为您提供一个文件流。在循环中,可以使用 fread() , fgets() fgetc() 正如ZARAWESOME的答案所示,然后处理该缓冲区或将其附加到C++字符串中。
    • 您可以使用 PONEN() ,然后使用非标准 __gnu_cxx::stdio_filebuf 要包装它,然后创建一个 std::istream stdio_filebuf 像其他C++流一样对待它。这是最类似C++的方法。这里是 part 1 part 2 这个方法的一个例子。
        5
  •  1
  •   Bill the Lizard    16 年前

    感谢所有花时间回答的人。一个同事把我指给 ostringstream 班级。下面是一些示例代码,它基本上完成了我在原始问题中所做的工作。

    #include <iostream> // cout
    #include <sstream> // ostringstream
    
    int main(int argc, char** argv) {
        FILE* stream = popen( "df", "r" );
        std::ostringstream output;
    
        while( !feof( stream ) && !ferror( stream ))
        {
            char buf[128];
            int bytesRead = fread( buf, 1, 128, stream );
            output.write( buf, bytesRead );
        }
        std::string result = output.str();
        std::cout << "<RESULT>" << std::endl << result << "</RESULT>" << std::endl;
        return (0);
    }
    
        6
  •  1
  •   Arkadiy    16 年前

    要在更新中回答问题:

    char buffer[1024];
    char * line = NULL;
    while ((line = fgets(buffer, sizeof buffer, fp)) != NULL) {
        // parse one line of df's output here.
    }
    

    这就够了吗?

        7
  •  0
  •   Timo Geusch    16 年前

    首先要检查的是bufsize的值-如果恰好是<=0,那么malloc可能会返回一个空值,因为此时您正试图分配一个大小为0的缓冲区。

    另一个解决方法是让malloc为您提供一个大小为n>=1的缓冲区(bufsize+n),它可以解决这个特定的问题。

    除此之外,你所发布的代码是纯C,而不是C++,所以包括过多。

        8
  •  0
  •   flolo    16 年前

    检查你的臀部尺寸。 ftell 错误时可以返回-1,这可能导致malloc不分配,缓冲区的值为空。

    原因是 位置函数 失败是因为教皇。你不能搜索管道。

        9
  •  0
  •   zaratustra    16 年前

    管道不是随机进入的。它们是连续的,这意味着一旦你读到一个字节,管道就不会再把它发送给你。很明显,这意味着你不能倒带。

    如果您只想将数据输出回用户,可以执行以下操作:

    // your file opening code
    
    while (!feof(fp))
    {
    char c = getc(fp);
    std::cout << c;
    }
    

    这将一个接一个地从df管道中拉出字节,并将它们直接泵入输出。

    现在,如果您想访问DF输出作为一个整体,您可以将它管到一个文件中并读取该文件,或者将输出连接到一个构造中,例如C++字符串。