una solicita sistema sirve que procesos proceso para operativo manejo llamadas llamada conclusion como archivos c++ linux operating-system system-calls

solicita - ¿Cómo leo los resultados de una llamada al sistema() en C++?



manejo de procesos en c (9)

Estoy usando el siguiente código para intentar leer los resultados de un comando df 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 está dando un "Error de memoria" con un estado de salida de ''2'', por lo que puedo ver dónde está fallando, simplemente no entiendo por qué .

Puse esto junto al código de ejemplo que encontré en Ubuntu Forums y C ++ Reference , así que no estoy casado con él. Si alguien puede sugerir una mejor manera de leer los resultados de una llamada a system (), estoy abierto a nuevas ideas.

EDITAR al original: Bien, bufSize está apareciendo negativo, y ahora entiendo por qué. No se puede acceder aleatoriamente a una tubería, como intenté ingenuamente hacer.

No puedo ser la primera persona en intentar hacer esto. ¿Puede alguien darme (o señalarme a) un ejemplo de cómo leer los resultados de una llamada al sistema () en una variable en C ++?


(Una nota sobre terminología: "llamada al sistema" en Unix y Linux generalmente se refiere a llamar a una función del kernel desde un código de espacio de usuario. Referirse a ella como "los resultados de un system() " o "los resultados de un system(3) call "sería más claro, pero probablemente sería mejor decir" capturando el resultado de un proceso ".)

De todos modos, puede leer la salida de un proceso tal como puede leer cualquier otro archivo. Específicamente:

  • Puede iniciar el proceso utilizando pipe() , fork() y exec() . Esto le da un descriptor de archivo, luego puede usar un bucle para read() desde el descriptor de archivo en un búfer y close() el descriptor de archivo una vez que haya terminado. Esta es la opción de nivel más bajo y le da el mayor control.
  • Puede comenzar el proceso usando popen() , como lo está haciendo. Esto le da una secuencia de archivos. En un bucle, puede leer el uso de la secuencia en una variable temporal o búfer usando fread() , fgets() o fgetc() , como lo demuestra la respuesta de Zarawesome, luego procese ese búfer o añádalo a una cadena de C ++.
  • Puede iniciar el proceso usando popen() , luego usar el __gnu_cxx :: stdio_filebuf no estándar para envolverlo, luego crear un std::istream del stdio_filebuf y tratarlo como cualquier otra transmisión C ++. Este es el enfoque más similar a C ++. Aquí está la parte 1 y la parte 2 de un ejemplo de este enfoque.

Las tuberías no son de acceso aleatorio. Son secuenciales, lo que significa que una vez que lee un byte, la tubería no se lo enviará nuevamente. Lo que significa, obviamente, que no puedes 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á los bytes del conducto 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 el resultado en una construcción como una cadena de C ++.


Lo primero que se debe comprobar es el valor de bufSize: si eso es <= 0, es probable que malloc devuelva un NULL mientras intenta asignar un buffer de tamaño 0 en ese punto.

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

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


No estoy seguro de que pueda fseek / ftell de transmisiones de tubería como esta.

¿Has comprobado el valor de bufSize? Una razón por la cual malloc está fallando es por los buffers increíblemente dimensionados.


revisa tu bufSize. ftell puede devolver -1 en caso de error, y esto puede llevar a una no asignación por parte de malloc con un buffer que tenga un valor NULL.

La razón por la que ftell falla es por el popen. No puedes buscar tuberías.


Estás haciendo esto demasiado difícil. popen (3) devuelve un FILE * antiguo normal FILE * para un archivo de tubería estándar, es decir, registros terminados en línea nueva. Puedes leerlo con muy alta eficiencia 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 incluso 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 un poco más de manejo de errores allí, pero esa es la idea. El punto es que usted trata el FILE * de popen como cualquier FILE * , y lo lee línea por línea.


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 básicamente hace lo que estaba intentando 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. }

¿Esto sería suficiente?


¿Por qué std::malloc() ?

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 firmar".

De acuerdo con la documentación , std::ftell() devuelve -1 en caso de falla. 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 de la salida del comando sin leerla, y solo puede leerla una vez. Tienes que leerlo en bloques, ya sea haciendo crecer tu memoria intermedia 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 probablemente usa df para obtener su información: statvfs() .