English 中文(简体)
我如何在C++中读取system()调用的结果?
原标题:
  • 时间:2008-11-21 17:06:41
  •  标签:

我正在使用以下代码尝试使用读取Linux中df命令的结果。

#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论坛C++参考找到的示例代码中整合出来的,因此我不会偏执于它。如果有人能建议更好的方式来读取system()调用的结果,我会很乐意接受新想法。

对原文进行编辑:好的,bufSize变成了负数,现在我明白为什么了。像我曾经天真地尝试的那样,你不能随意访问一个管道。

我肯定不是第一个尝试做这件事的人。有人能否给我一个(或指向我一个)在C++中将系统()调用的结果读取到变量中的示例?

最佳回答

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

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

根据文档std::ftell()在失败时返回-1。很明显,它会失败的一个原因是你不能在管道或FIFO中进行寻址

没有逃脱的可能;你无法在不阅读输出的情况下知道命令输出的长度,而且你只能阅读一次。你需要分块阅读,要么根据需要增加缓冲区大小,要么动态解析。

但是,你当然可以通过直接使用系统调用 statvfs(),可能会用于获取其信息的df来避免整个问题。

问题回答

你让这一切变得太难了。popen(3)返回一个正常的旧FILE *,用于标准管道文件,也就是,换行结束记录。你可以通过在C中使用fgets(3)来以非常高效的方式读取它:

#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
}

那里有更多的错误处理,但这就是想法。重点是您要像处理任何FILE *一样处理popen中的FILE *,并逐行阅读它。

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

无论如何,您可以像读取任何其他文件一样阅读进程的输出。具体来说:

  • You can start the process using pipe(), fork(), and exec(). This gives you a file descriptor, then you can use a loop to read() from the file descriptor into a buffer and close() the file descriptor once you re done. This is the lowest level option and gives you the most control.
  • You can start the process using popen(), as you re doing. This gives you a file stream. In a loop, you can read using from the stream into a temporary variable or buffer using fread(), fgets(), or fgetc(), as Zarawesome s answer demonstrates, then process that buffer or append it to a C++ string.
  • You can start the process using popen(), then use the nonstandard __gnu_cxx::stdio_filebuf to wrap that, then create an std::istream from the stdio_filebuf and treat it like any other C++ stream. This is the most C++-like approach. Here s part 1 and part 2 of an example of this approach.

我不确定你是否可以像这样在管道流中使用fseek / ftell。

你检查了bufSize的值吗? malloc失败的一个原因是因为缓冲区的大小超出了实际需要。

感谢所有抽出时间回答的人。一位同事指引我到了 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);
}

回答更新中的问题:

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

这样足够吗?

首先要检查的是bufSize的值 - 如果它小于等于0,那么很有可能malloc会返回一个NULL,因为此时你尝试分配大小为0的缓冲区。

另一个解决方法是要求malloc提供您一个大小为(bufSize + n)的缓冲区,其中n> = 1,这应该解决这个特定的问题。

除此之外,您发布的代码纯粹是C,所以包括 有些过头了。

请检查您的bufSize。ftell可能因错误返回-1,这可能导致malloc无法分配具有NULL值的缓冲区。

ftell 失败的原因是因为 popen,无法在管道中搜索。

管道不是随机访问的。它们是顺序的,这意味着一旦你读取了一个字节,管道就不会再次将其发送给您。这意味着,显然,您无法倒回它。

如果您只想将数据输出回用户,您可以这样做:

// your file opening code

while (!feof(fp))
{
char c = getc(fp);
std::cout << c;
}

这将逐个从df管道中挑选出字节,并将它们直接输送到输出中。

现在,如果你想要整体访问 df 的输出,你可以将其管道到一个文件并读取该文件,或将输出连接成一个结构,例如一个 C++ 字符串。





相关问题
热门标签