Domanda

Sto usando il seguente codice per provare a leggere i risultati di a df comando in Linux utilizzando 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);
}

Questo codice mi restituisce un "Errore di memoria" con uno stato di uscita pari a "2", quindi posso vederlo Dove sta fallendo, proprio non capisco Perché.

L'ho messo insieme dal codice di esempio che ho trovato su Forum di Ubuntu E Riferimento C++, quindi non sono sposato con esso.Se qualcuno può suggerire un modo migliore per leggere i risultati di una chiamata system(), sono aperto a nuove idee.

EDIT all'originale: Va bene, bufSize risulta negativo e ora capisco il motivo.Non è possibile accedere in modo casuale a una pipe, come ho ingenuamente provato a fare.

Non posso essere la prima persona a provare a farlo.Qualcuno può fornire (o indicarmi) un esempio di come leggere i risultati di una chiamata system() in una variabile in C++?

È stato utile?

Soluzione

Perché std::malloc() fallirebbe

Il motivo ovvio è " perché std::ftell() ha restituito un numero con segno negativo, che è stato quindi trattato come un enorme numero senza segno " ;.

Secondo la documentazione , df restituisce -1 in caso di fallimento. Una ovvia ragione per cui fallirebbe che non puoi cercare in una pipe o FIFO .

Non c'è scampo; non puoi conoscere la lunghezza dell'output del comando senza leggerlo e puoi leggerlo solo una volta. Devi leggerlo a pezzi, o aumentando il buffer in base alle esigenze o analizzando al volo.

Ma, ovviamente, puoi semplicemente evitare l'intero problema utilizzando direttamente la chiamata di sistema che statvfs() probabilmente utilizza per ottenere le sue informazioni: <=>.

Altri suggerimenti

Stai rendendo tutto troppo difficile. papa(3) restituisce un valore normale vecchio FILE * per un file pipe standard, vale a dire record terminati con nuova riga.Puoi leggerlo con altissima efficienza utilizzando fget(3) così in 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
}

In C++ è ancora più semplice:

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

C'è qualche ulteriore gestione degli errori lì, ma questa è l'idea.Il punto è che tratti il FILE * da papa proprio come Qualunque FILE *, e leggilo riga per riga.

Non sono sicuro che puoi cercare stream di pipe fseek / ftell in questo modo.

Hai verificato il valore di bufSize? Uno dei motivi per cui malloc non riesce è per buffer di dimensioni folle.

(Una nota sulla terminologia: " chiamata di sistema " in Unix e Linux generalmente si riferisce alla chiamata di una funzione del kernel dal codice dello spazio utente. Riferendosi ad essa come " i risultati di un system() call " o " i risultati di un system(3) call " sarebbero più chiari, ma probabilmente sarebbe meglio semplicemente dire " catturare l'output di un processo. ")

Ad ogni modo, puoi leggere l'output di un processo proprio come puoi leggere qualsiasi altro file. In particolare:

  • Puoi avviare il processo usando pipe(), fork() e exec(). Questo ti dà un descrittore di file, quindi puoi usare un ciclo per read() dal descrittore di file in un buffer e close() il descrittore di file una volta terminato. Questa è l'opzione di livello più basso e ti dà il massimo controllo.
  • Puoi avviare il processo usando popen(), come stai facendo. Questo ti dà un flusso di file. In un ciclo, puoi leggere usando dallo stream in una variabile temporanea o buffer usando fread(), fgets() o fgetc(), come dimostra la risposta di Zarawesome, quindi elaborare quel buffer o aggiungerlo a una stringa C ++.
  • È possibile avviare il processo utilizzando std::istream, quindi utilizzare __ gnu_cxx :: stdio_filebuf per racchiuderlo, quindi creare un stdio_filebuf dal <=> e trattarlo come qualsiasi altro flusso C ++. Questo è l'approccio più simile al C ++. Ecco parte 1 e parte 2 di un esempio di questo approccio.

Grazie a tutti coloro che hanno avuto il tempo di rispondere. Un collega mi ha indicato la ostringstream . Ecco un codice di esempio che fa essenzialmente quello che stavo tentando di fare nella domanda originale.

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

Per rispondere alla domanda nell'aggiornamento:

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

Sarebbe abbastanza?

La prima cosa da verificare è il valore di bufSize - se ciò accade < = 0, è probabile che malloc restituisca un NULL mentre si sta tentando di allocare un buffer di dimensione 0 in quel punto.

Un'altra soluzione alternativa potrebbe essere quella di chiedere a malloc di fornire un buffer delle dimensioni (bufSize + n) con n > = 1, che dovrebbe aggirare questo particolare problema.

A parte questo, il codice che hai pubblicato è puro C, non C ++, quindi includerlo è un po 'esagerato.

controlla il tuo bufSize. ftell può restituire -1 in caso di errore e ciò può comportare la non allocazione da parte di malloc con buffer con valore NULL.

Il motivo del fallimento di <=> è dovuto al popen. Non puoi cercare le pipe.

I tubi non hanno accesso casuale. Sono sequenziali, il che significa che una volta letto un byte, la pipe non te lo invierà più. Il che significa, ovviamente, che non puoi riavvolgerlo.

Se vuoi solo restituire i dati all'utente, puoi semplicemente fare qualcosa del tipo:

// your file opening code

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

Questo estrarrà i byte dal df pipe, uno per uno, e li pomperà direttamente nell'output.

Ora, se si desidera accedere all'output df nel suo insieme, è possibile reindirizzarlo in un file e leggere quel file oppure concatenare l'output in un costrutto come una stringa C ++.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top