如何在C ++中读取system()调用的结果?

时间:2022-09-01 16:40:21

I'm using the following code to try to read the results of a df command in Linux using popen.

我正在使用以下代码尝试使用popen在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);
}

This code is giving me a "Memory error" with an exit status of '2', so I can see where it's failing, I just don't understand why.

这段代码给我一个“内存错误”,退出状态为“2”,所以我可以看到它失败的地方,我只是不明白为什么。

I put this together from example code that I found on Ubuntu Forums and C++ Reference, so I'm not married to it. If anyone can suggest a better way to read the results of a system() call, I'm open to new ideas.

我从Ubuntu论坛和C ++参考中找到的示例代码中将它放在一起,所以我没有和它结婚。如果有人可以建议更好的方式来阅读system()调用的结果,我会接受新的想法。

EDIT to the original: Okay, bufSize is coming up negative, and now I understand why. You can't randomly access a pipe, as I naively tried to do.

编辑到原文:好的,bufSize即将消极,现在我理解为什么。您不能随意访问管道,因为我天真地试图这样做。

I can't be the first person to try to do this. Can someone give (or point me to) an example of how to read the results of a system() call into a variable in C++?

我不能成为第一个尝试这样做的人。有人可以给(或指向我)一个如何在C ++中读取system()调用结果的示例吗?

9 个解决方案

#1


5  

Why would std::malloc() fail?

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

The obvious reason is "because std::ftell() returned a negative signed number, which was then treated as a huge unsigned number".

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

According to the documentation, std::ftell() returns -1 on failure. One obvious reason it would fail is that you cannot seek in a pipe or FIFO.

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

There is no escape; you cannot know the length of the command output without reading it, and you can only read it once. You have to read it in chunks, either growing your buffer as needed or parsing on the fly.

跑不了的;如果不读取它就无法知道命令输出的长度,并且只能读取一次。您必须以块的形式阅读它,或者根据需要增长缓冲区或者动态解析。

But, of course, you can simply avoid the whole issue by directly using the system call df probably uses to get its information: statvfs().

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

#2


6  

You're making this all too hard. popen(3) returns a regular old FILE * for a standard pipe file, which is to say, newline terminated records. You can read it with very high efficiency by using fgets(3) like so in C:

你这太难了。 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
}

In C++ it's even easier --

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

There's some more error handling there, but that's the idea. The point is that you treat the FILE * from popen just like any FILE *, and read it line by line.

那里有更多的错误处理,但这就是想法。关键是你从popen处理FILE *就像任何FILE *一样,并逐行读取。

#3


3  

I'm not sure you can fseek/ftell pipe streams like this.

我不确定你能不能像这样fseek / ftell管道流。

Have you checked the value of bufSize ? One reason malloc be failing is for insanely sized buffers.

你检查过bufSize的价值了吗? malloc失败的一个原因是疯狂大小的缓冲区。

#4


3  

(A note on terminology: "system call" in Unix and Linux generally refers to calling a kernel function from user-space code. Referring to it as "the results of a system() call" or "the results of a system(3) call" would be clearer, but it would probably be better to just say "capturing the output of a process.")

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

Anyway, you can read a process's output just like you can read any other file. Specifically:

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

  • 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.
  • 您可以使用pipe(),fork()和exec()来启动该过程。这为您提供了文件描述符,然后您可以使用循环从文件描述符中读取()到缓冲区中,并在完成后关闭()文件描述符。这是最低级别选项,为您提供最大程度的控制。

  • 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.
  • 您可以使用popen()开始此过程,就像您正在做的那样。这为您提供了一个文件流。在循环中,您可以使用fread(),fgets()或fgetc()从流中读取临时变量或缓冲区,如Zarawesome的答案所示,然后处理该缓冲区或将其附加到C ++字符串。

  • 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.
  • 您可以使用popen()启动该过程,然后使用非标准的__gnu_cxx :: stdio_filebuf来包装它,然后从stdio_filebuf创建一个std :: istream并将其视为任何其他C ++流。这是最类似C ++的方法。这是此方法示例的第1部分和第2部分。

#5


1  

Thanks to everyone who took the time to answer. A co-worker pointed me to the ostringstream class. Here's some example code that does essentially what I was attempting to do in the original question.

感谢所有花时间回答的人。一位同事向我指出了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  

To answer the question in the update:

要回答更新中的问题:

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

Would this be enough?

这够了吗?

#7


0  

First thing to check is the value of bufSize - if that happens to be <= 0, chances are that malloc returns a NULL as you're trying to allocate a buffer of size 0 at that point.

首先要检查的是bufSize的值 - 如果碰巧是<= 0,那么当你尝试在那时分配一个大小为0的缓冲区时,malloc可能会返回NULL。

Another workaround would be to ask malloc to provide you with a buffer of the size (bufSize + n) with n >= 1, which should work around this particular problem.

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

That aside, the code you posted is pure C, not C++, so including is overdoing it a little.

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

#8


0  

check your bufSize. ftell can return -1 on error, and this can lead to nonallocation by malloc with buffer having a NULL value.

检查你的bufSize。 ftell可以在出错时返回-1,这可能会导致malloc与具有NULL值的缓冲区进行非分配。

The reason for the ftell to fail is, because of the popen. You cant search pipes.

由于popen,ftell失败的原因是。你无法搜索管道。

#9


0  

Pipes are not random access. They're sequential, which means that once you read a byte, the pipe is not going to send it to you again. Which means, obviously, you can't rewind it.

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

If you just want to output the data back to the user, you can just do something like:

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

// your file opening code

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

This will pull bytes out of the df pipe, one by one, and pump them straight into the output.

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

Now if you want to access the df output as a whole, you can either pipe it into a file and read that file, or concatenate the output into a construct such as a C++ String.

现在,如果要整体访问df输出,可以将其传输到文件中并读取该文件,或者将输出连接到诸如C ++ String之类的构造中。

#1


5  

Why would std::malloc() fail?

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

The obvious reason is "because std::ftell() returned a negative signed number, which was then treated as a huge unsigned number".

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

According to the documentation, std::ftell() returns -1 on failure. One obvious reason it would fail is that you cannot seek in a pipe or FIFO.

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

There is no escape; you cannot know the length of the command output without reading it, and you can only read it once. You have to read it in chunks, either growing your buffer as needed or parsing on the fly.

跑不了的;如果不读取它就无法知道命令输出的长度,并且只能读取一次。您必须以块的形式阅读它,或者根据需要增长缓冲区或者动态解析。

But, of course, you can simply avoid the whole issue by directly using the system call df probably uses to get its information: statvfs().

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

#2


6  

You're making this all too hard. popen(3) returns a regular old FILE * for a standard pipe file, which is to say, newline terminated records. You can read it with very high efficiency by using fgets(3) like so in C:

你这太难了。 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
}

In C++ it's even easier --

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

There's some more error handling there, but that's the idea. The point is that you treat the FILE * from popen just like any FILE *, and read it line by line.

那里有更多的错误处理,但这就是想法。关键是你从popen处理FILE *就像任何FILE *一样,并逐行读取。

#3


3  

I'm not sure you can fseek/ftell pipe streams like this.

我不确定你能不能像这样fseek / ftell管道流。

Have you checked the value of bufSize ? One reason malloc be failing is for insanely sized buffers.

你检查过bufSize的价值了吗? malloc失败的一个原因是疯狂大小的缓冲区。

#4


3  

(A note on terminology: "system call" in Unix and Linux generally refers to calling a kernel function from user-space code. Referring to it as "the results of a system() call" or "the results of a system(3) call" would be clearer, but it would probably be better to just say "capturing the output of a process.")

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

Anyway, you can read a process's output just like you can read any other file. Specifically:

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

  • 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.
  • 您可以使用pipe(),fork()和exec()来启动该过程。这为您提供了文件描述符,然后您可以使用循环从文件描述符中读取()到缓冲区中,并在完成后关闭()文件描述符。这是最低级别选项,为您提供最大程度的控制。

  • 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.
  • 您可以使用popen()开始此过程,就像您正在做的那样。这为您提供了一个文件流。在循环中,您可以使用fread(),fgets()或fgetc()从流中读取临时变量或缓冲区,如Zarawesome的答案所示,然后处理该缓冲区或将其附加到C ++字符串。

  • 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.
  • 您可以使用popen()启动该过程,然后使用非标准的__gnu_cxx :: stdio_filebuf来包装它,然后从stdio_filebuf创建一个std :: istream并将其视为任何其他C ++流。这是最类似C ++的方法。这是此方法示例的第1部分和第2部分。

#5


1  

Thanks to everyone who took the time to answer. A co-worker pointed me to the ostringstream class. Here's some example code that does essentially what I was attempting to do in the original question.

感谢所有花时间回答的人。一位同事向我指出了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  

To answer the question in the update:

要回答更新中的问题:

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

Would this be enough?

这够了吗?

#7


0  

First thing to check is the value of bufSize - if that happens to be <= 0, chances are that malloc returns a NULL as you're trying to allocate a buffer of size 0 at that point.

首先要检查的是bufSize的值 - 如果碰巧是<= 0,那么当你尝试在那时分配一个大小为0的缓冲区时,malloc可能会返回NULL。

Another workaround would be to ask malloc to provide you with a buffer of the size (bufSize + n) with n >= 1, which should work around this particular problem.

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

That aside, the code you posted is pure C, not C++, so including is overdoing it a little.

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

#8


0  

check your bufSize. ftell can return -1 on error, and this can lead to nonallocation by malloc with buffer having a NULL value.

检查你的bufSize。 ftell可以在出错时返回-1,这可能会导致malloc与具有NULL值的缓冲区进行非分配。

The reason for the ftell to fail is, because of the popen. You cant search pipes.

由于popen,ftell失败的原因是。你无法搜索管道。

#9


0  

Pipes are not random access. They're sequential, which means that once you read a byte, the pipe is not going to send it to you again. Which means, obviously, you can't rewind it.

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

If you just want to output the data back to the user, you can just do something like:

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

// your file opening code

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

This will pull bytes out of the df pipe, one by one, and pump them straight into the output.

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

Now if you want to access the df output as a whole, you can either pipe it into a file and read that file, or concatenate the output into a construct such as a C++ String.

现在,如果要整体访问df输出,可以将其传输到文件中并读取该文件,或者将输出连接到诸如C ++ String之类的构造中。