Вопрос

Я использую следующий код, чтобы попытаться прочитать результаты 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 ++.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top