Pregunta

Estoy usando el siguiente código para leer los resultados de un df comando en Linux usando 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);
}

Este código me da un "error de Memoria" con un estado de salida de '2', por lo que puedo ver donde es fracasar, simplemente no entiendo por qué.

Pongo este conjunto de código de ejemplo que he encontrado en Foros De Ubuntu y Referencia De C++ , así que no estoy casado con ella.Si alguien puede sugerir la mejor manera de leer los resultados de una llamada system (), estoy abierto a nuevas ideas.

EDITAR con el original: Bueno, bufSize se acerca negativo, y ahora entiendo por qué.Usted no puede tener acceso de forma aleatoria de una tubería, como yo, ingenuamente, trató de hacer.

No puedo ser la primera persona a tratar de hacer esto.Alguien puede dar (o me) un ejemplo de cómo leer los resultados de una llamada system() en una variable en C++?

¿Fue útil?

Solución

¿Por qué std::malloc() fallaría?

La razón obvia es " porque std::ftell() devolvió un número con signo negativo, que luego se trató como un gran número sin signo " ;.

De acuerdo con la documentación , df devuelve -1 en caso de fracaso Una razón obvia por la que fallaría es que no se puede buscar en una tubería o FIFO .

No hay escapatoria; no puede conocer la longitud del resultado del comando sin leerlo, y solo puede leerlo una vez. Tienes que leerlo en fragmentos, ya sea haciendo crecer tu buffer según sea necesario o analizando sobre la marcha.

Pero, por supuesto, simplemente puede evitar todo el problema utilizando directamente la llamada al sistema que statvfs() probablemente utiliza para obtener su información: <=>.

Otros consejos

Estás haciendo esto demasiado difícil. popen (3) devuelve un FILE * antiguo normal para un archivo de canalización estándar, es decir, registros terminados en nueva línea. Puede leerlo con una eficiencia muy alta usando fgets (3) como en 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
}

En C ++ es aún más fácil:

#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
}

Hay más manejo de errores allí, pero esa es la idea. El punto es que trata el <=> desde popen como any <=>, y lo lee línea por línea.

No estoy seguro de que pueda fseek / ftell tuberías de este tipo.

¿Ha verificado el valor de bufSize? Una razón por la que Malloc está fallando es por buffers de tamaño increíble.

(Una nota sobre la terminología:"el sistema de llamada" en Unix y Linux en general se refiere a la convocatoria del núcleo de la función de espacio de usuario código.Refiriéndose a ella como "los resultados de un system() la llamada" o "los resultados de un system(3) la llamada" sería más clara, pero probablemente sería mejor decir "la captura de la salida de un proceso.")

De todos modos, se puede leer de un proceso de salida al igual que usted puede leer cualquier otro archivo.Específicamente:

  • Usted puede comenzar el proceso de uso de pipe(), fork(), y exec().Esto le da un descriptor de archivo, entonces usted puede utilizar un bucle para read() desde el descriptor de archivo en un buffer y close() el descriptor de archivo una vez que haya terminado.Este es el nivel más bajo de la opción y le da el máximo control.
  • Usted puede comenzar el proceso de uso de popen(), como estás haciendo.Esto le da una secuencia de archivo.En un bucle, se puede leer usando la secuencia en una variable temporal o buffer usando fread(), fgets(), o fgetc(), como Zarawesome la respuesta de la muestra, a continuación, proceso que búfer o añadirlo a una cadena C++.
  • Usted puede comenzar el proceso de uso de popen(), a continuación, utilizar la no estándar __gnu_cxx::stdio_filebuf para envolver que, a continuación, crear un std::istream a partir de la stdio_filebuf y tratarlo como a cualquier otro C++ corriente.Este es el más C++-como en el enfoque.Aquí parte 1 y parte 2 un ejemplo de este enfoque.

Gracias a todos los que se tomaron el tiempo para responder. Un compañero de trabajo me señaló la clase ostringstream . Aquí hay un código de ejemplo que hace esencialmente lo que intentaba hacer en la pregunta original.

#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);
}

Para responder la pregunta en la actualización:

char buffer[1024];
char * line = NULL;
while ((line = fgets(buffer, sizeof buffer, fp)) != NULL) {
    // parse one line of df's output here.
}

¿Sería esto suficiente?

Lo primero que se debe verificar es el valor de bufSize: si eso es < = 0, lo más probable es que malloc devuelva un NULL mientras intenta asignar un búfer de tamaño 0 en ese punto.

Otra solución alternativa sería pedirle a malloc que le proporcione un búfer del tamaño (bufSize + n) con n > = 1, que debería solucionar este problema en particular.

Aparte de eso, el código que publicaste es puro C, no C ++, por lo que incluirlo está exagerando un poco.

comprueba tu tamaño de buf. ftell puede devolver -1 en caso de error, y esto puede conducir a la no asignación por malloc con un búfer que tiene un valor NULL.

La razón por la cual el <=> falla es, debido al popen. No puedes buscar tuberías.

Las tuberías no son de acceso aleatorio. Son secuenciales, lo que significa que una vez que lea un byte, la tubería no se lo enviará nuevamente. Lo que significa, obviamente, que no puede rebobinarlo.

Si solo desea devolver los datos al usuario, puede hacer algo como:

// your file opening code

while (!feof(fp))
{
char c = getc(fp);
std::cout << c;
}

Esto extraerá bytes de la tubería df, uno por uno, y los bombeará directamente a la salida.

Ahora, si desea acceder a la salida de df como un todo, puede canalizarlo en un archivo y leer ese archivo, o concatenar la salida en una construcción como una cadena de C ++.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top