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