Как мне прочитать результаты вызова system() на C ++?
-
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 *
для стандартного конвейерного файла, то есть записей, заканчивающихся переводом строки Вы можете прочитать его с очень высокой эффективностью, используя 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 так же, как любой <=>, и читаете его построчно.
Я не уверен, что вы можете подобрать такие потоковые потоки, как этот.
Вы проверили значение bufSize? Одна из причин сбоя malloc - буферы безумного размера.
(Примечание по терминологии:"системный вызов" в Unix и Linux обычно относится к вызову функции ядра из пользовательского кода.Ссылаясь на это как на "результаты system()
вызов" или "результаты system(3)
вызов" было бы понятнее, но, вероятно, было бы лучше просто сказать "захват выходных данных процесса".)
В любом случае, вы можете прочитать выходные данные процесса точно так же, как вы можете прочитать любой другой файл.В частности:
- Вы можете начать этот процесс с помощью
pipe()
,fork()
, иexec()
.Это дает вам файловый дескриптор, затем вы можете использовать цикл дляread()
из файлового дескриптора в буфер иclose()
укажите дескриптор файла, как только вы закончите.Это опция самого низкого уровня, которая дает вам максимальный контроль. - Вы можете начать этот процесс с помощью
popen()
, как ты это делаешь.Это дает вам файловый поток.В цикле вы можете считывать using из потока во временную переменную или буфер, используя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 - если это < = 0, есть вероятность, что malloc вернет NULL, когда вы пытаетесь выделить буфер размера 0 в этой точке. р>
Другой обходной путь - попросить malloc предоставить вам буфер размером (bufSize + n) с n > = 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 ++.