我正在使用以下代码尝试使用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 ++中将system()调用的结果读入变量的示例吗?
答案 0 :(得分:6)
你这太难了。 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
}
那里有更多的错误处理,但这就是主意。关键是您将 popen 中的FILE *
视为任何 FILE *
,并逐行阅读。
答案 1 :(得分:5)
为什么std::malloc()
会失败?
显而易见的原因是“因为std::ftell()
返回了一个负的有符号数字,然后被视为一个巨大的无符号数字”。
根据the documentation,std::ftell()
失败时返回-1。它失败的一个明显原因是你无法在管道或FIFO中寻找。
没有逃脱;如果不读取它就无法知道命令输出的长度,并且只能读取一次。你必须以块的形式阅读它,或者根据需要增长缓冲区或者动态解析。
但是,当然,您可以直接使用系统调用df
来获取其信息来避免整个问题:statvfs()
。
答案 2 :(得分:3)
我不确定你是否可以像这样fseek / ftell管道流。
您检查过bufSize的值吗? malloc失败的一个原因是疯狂大小的缓冲区。
答案 3 :(得分:3)
(关于术语的说明:Unix和Linux中的“系统调用”通常是指从用户空间代码调用内核函数。将其称为“system()
调用的结果”或“结果”一个system(3)
调用“会更清楚,但最好只说”捕获一个进程的输出。“)
无论如何,您可以读取进程的输出,就像您可以读取任何其他文件一样。具体做法是:
pipe()
,fork()
和exec()
开始此过程。这为您提供了文件描述符,然后您可以使用从文件描述符到read()
的循环到缓冲区,并在完成后使用close()
文件描述符。这是最低级别选项,为您提供最大程度的控制。popen()
开始此过程,就像您正在做的那样。这为您提供了一个文件流。在循环中,您可以使用fread()
,fgets()
或fgetc()
从流中读取临时变量或缓冲区,如Zarawesome的答案所示,然后处理该缓冲区或将其附加到一个C ++字符串。popen()
启动该过程,然后使用非标准__gnu_cxx::stdio_filebuf对其进行换行,然后从std::istream
创建stdio_filebuf
并像对待任何其他C ++一样对待它流。这是最类似C ++的方法。以下是此方法示例的part 1和part 2。答案 4 :(得分:1)
回答更新中的问题:
char buffer[1024];
char * line = NULL;
while ((line = fgets(buffer, sizeof buffer, fp)) != NULL) {
// parse one line of df's output here.
}
这还够吗?
答案 5 :(得分:1)
感谢所有花时间回答的人。一位同事指着我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 :(得分:0)
检查你的bufSize。 ftell
可以在出错时返回-1,这可能会导致malloc使用具有NULL值的缓冲区进行非分配。
ftell
失败的原因是,因为popen。你无法搜索管道。
答案 7 :(得分:0)
要检查的第一件事是bufSize的值 - 如果恰好是&lt; = 0,那么当你试图在那时分配一个大小为0的缓冲区时,malloc可能会返回NULL。
另一个解决方法是让malloc为你提供一个大小为(bufSize + n)的缓冲区,其中n> = 1,这应该解决这个特殊问题。
除此之外,你发布的代码是纯C而不是C ++,所以包括过度使用它。
答案 8 :(得分:0)
管道不是随机访问。它们是顺序的,这意味着一旦你读取一个字节,管道就不会再发送给你了。显然,这意味着你无法倒回它。
如果您只想将数据输出回用户,您可以执行以下操作:
// your file opening code
while (!feof(fp))
{
char c = getc(fp);
std::cout << c;
}
这将逐个从df管道中拉出字节,并将它们直接输入输出。
现在,如果要整体访问df输出,可以将其传输到文件中并读取该文件,或者将输出连接到诸如C ++ String之类的构造中。