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++?
To answer the question in the update:
Would this be enough?
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.
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.(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 asystem(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:
pipe()
,fork()
, andexec()
. This gives you a file descriptor, then you can use a loop toread()
from the file descriptor into a buffer andclose()
the file descriptor once you're done. This is the lowest level option and gives you the most control.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 usingfread()
,fgets()
, orfgetc()
, as Zarawesome's answer demonstrates, then process that buffer or append it to a C++ string.popen()
, then use the nonstandard __gnu_cxx::stdio_filebuf to wrap that, then create anstd::istream
from thestdio_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.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:In C++ it's even easier --
There's some more error handling there, but that's the idea. The point is that you treat the
FILE *
from popen just like anyFILE *
, and read it line by line.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.