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

发布于 2024-07-08 22:59:56 字数 1658 浏览 5 评论 0原文

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

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

我将这些示例代码放在 Ubuntu 论坛C++ 参考,所以我不喜欢它。 如果有人可以建议一种更好的方法来读取 system() 调用的结果,我愿意接受新想法。

编辑原始内容:好吧,bufSize出现负值,现在我明白为什么了。 你不能像我天真地尝试那样随机访问管道。

我不可能是第一个尝试这样做的人。 有人可以给出(或指出)一个如何将 system() 调用的结果读取到 C++ 变量中的示例吗?

I'm using the following code to try to read the results of a df command in Linux using 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);
}

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.

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.

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.

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++?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

屋顶上的小猫咪 2024-07-15 22:59:56

你让这一切变得太难了。 popen(3) 返回标准管道文件的常规旧FILE *,也就是说,换行符终止的记录。 您可以通过使用 fgets(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
}

那里有更多的错误处理,但这就是想法。 要点是,您对待 popen 中的 FILE * 就像 any FILE * 一样,并逐行读取它线。

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:

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

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

゛时过境迁 2024-07-15 22:59:56

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

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

根据 文档std::ftell()失败时返回-1。 它失败的一个明显原因是您无法在管道或 FIFO 中查找

跑不了的; 不读命令输出就无法知道它的长度,而且只能读一次。 您必须分块读取它,要么根据需要增加缓冲区,要么动态解析。

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

Why would std::malloc() fail?

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

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.

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().

荒芜了季节 2024-07-15 22:59:56

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

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

  • 您可以使用 pipe()fork()exec() 启动该进程。 这将为您提供一个文件描述符,然后您可以使用循环将文件描述符中的 read() 读取到缓冲区中,并在完成后 close() 文件描述符。 这是最低级别的选项,为您提供最大的控制权。
  • 您可以像您正在做的那样使用 popen() 启动该过程。 这给你一个文件流。 在循环中,您可以使用 fread()fgets()fgetc() 从流中读取数据到临时变量或缓冲区中>,正如 Zarawesome 的答案所示,然后处理该缓冲区或将其附加到 C++ 字符串。
  • 您可以使用 popen() 启动该过程,然后使用非标准 __gnu_cxx::stdio_filebuf 来包装它,然后从 stdio_filebuf 创建一个 std::istream 并将其视为任何其他 C++ 流。 这是最像 C++ 的方法。 这是第 1 部分第 2 部分 这种方法的一个示例。

(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.")

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.
  • 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.
贱人配狗天长地久 2024-07-15 22:59:56

我不确定您是否可以像这样 fseek/ftell 管道流。

您检查过 bufSize 的值吗? malloc 失败的原因之一是缓冲区大小过大。

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

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

夕色琉璃 2024-07-15 22:59:56

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

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.

#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);
}
染墨丶若流云 2024-07-15 22:59:56

回答更新中的问题:

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

这足够了吗?

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?

撩起发的微风 2024-07-15 22:59:56

首先要检查的是 bufSize 的值 - 如果该值恰好 <= 0,则 malloc 很可能会返回 NULL,因为此时您正尝试分配大小为 0 的缓冲区。

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

除此之外,您发布的代码是纯 C,而不是 C++,因此包含它有点过头了。

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.

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.

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

我不吻晚风 2024-07-15 22:59:56

检查你的 bufSize。 ftell 出错时可能返回 -1,这可能会导致 malloc 未分配缓冲区具有 NULL 值的情况。

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

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

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

天冷不及心凉 2024-07-15 22:59:56

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

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

// your file opening code

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

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

现在,如果您想访问整个 df 输出,您可以将其通过管道传输到文件中并读取该文件,或者将输出连接到 C++ 字符串等构造中。

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文