2015-08-16 9 views
10

Sto scrivendo un programma per bambini per la pratica. Quello che sto cercando di realizzare è fondamentalmente una semplice piccola GUI che visualizza i servizi (per Linux); con i pulsanti per avviare, arrestare, abilitare e disabilitare i servizi (in modo simile alla scheda "Servizi" di msconfig in Windows). Sto usando C++ con Qt Creator su Fedora 21.Restituzione dell'output da script di bash alla chiamata della funzione C++

Voglio creare la GUI con C++ e compilare la GUI con l'elenco dei servizi chiamando gli script di bash e chiamando gli script di bash sui clic del pulsante per eseguire l'azione appropriata (abilitare, disabilitare, ecc.)

Ma quando la GUI C++ chiama lo script di bash (utilizzando system("path/to/script.sh")) il valore di ritorno è solo per il successo dell'uscita. Come posso ricevere l'output dello script stesso, in modo che possa a sua volta usarlo per visualizzare sulla GUI?

Per un esempio concettuale: se provassi a visualizzare l'output di (systemctl --type service | cut -d " " -f 1) in una GUI che ho creato in C++, come farei per farlo? Questo è anche il modo corretto di fare ciò che sto cercando di realizzare? In caso contrario,

  1. Qual è la strada giusta? e
  2. C'è ancora un modo per farlo usando il mio metodo attuale?

Ho cercato una soluzione a questo problema, ma non riesco a trovare le informazioni su come restituire i valori da Bash a C++, ma solo come chiamata script Bash da C++.

+0

Benvenuti in Stack Overflow! Ho modificato la tua domanda per correggere la formattazione e renderla più chiara/concisa. Grazie per aver fornito tanti dettagli! Si prega di consultare [chiedere] e [aiuto] se avete domande su come porre domande. – jtbandes

+0

Anche se apprezzo il tuo entusiasmo che ha assunto l'incarico di scrivere la tua app in C/C++ per chiamare 'systemctl', potresti dare un'occhiata a' zenity' o 'kdialog' della GUI (a seconda del desktop), che sono progettati per fornire una semplice interfaccia GUI ai comandi della shell. Anche se si finisce per scrivere il codice in C++, 'zenity/kdialog' è utile per la prototipazione. –

risposta

8

Abbiamo intenzione di sfruttare la funzione popen, qui.

std::string exec(char* cmd) { 
    FILE* pipe = popen(cmd, "r"); 
    if (!pipe) return "ERROR"; 
    char buffer[128]; 
    std::string result = ""; 
    while(!feof(pipe)) { 
     if(fgets(buffer, 128, pipe) != NULL) 
      result += buffer; 
    } 
    pclose(pipe); 
    return result; 
} 

Questa funzione prende un comando come argomento, e restituisce l'output come string.

NOTA: questo sarà non acquisizione stderr! Una soluzione rapida e semplice è quella di reindirizzare stderr a stdout, con 2>&1 alla fine del comando.

Here è la documentazione su popen. Codifica felice :)

+0

Grazie mille, è perfetto! Breve, conciso e chiaro. Fantastico grazie ancora! – Rhurac

+0

@Rhurac, Nessun problema, e benvenuto su StackOverflow! Goditi la permanenza! – MeetTitan

4

È necessario eseguire i comandi utilizzando popen anziché system e quindi eseguire il ciclo del puntatore del file restituito.

Ecco un semplice esempio per il comando ls -l

#include <stdio.h> 
#include <stdlib.h> 

int main() { 
    FILE *process; 
    char buff[1024]; 

    process = popen("ls -l", "r"); 

    if (process != NULL) { 
     while (!feof(process)) { 
      fgets(buff, sizeof(buff), process); 
      printf("%s", buff); 
     } 

     pclose(process); 
    } 

    return 0; 
} 
+1

Sì, ma se qualcuno copia pasta il tuo codice, e non termina dove pensavi che sarebbe, risulterebbe nei processi di zombi. Non fa mai male essere espliciti! :) – MeetTitan

+0

Grazie per la rapida risposta è stato fantastico grazie! – Rhurac

+0

L'utilizzo di 'while (! Feof (processo))' comporterà la duplicazione della riga finale di output. Il modo corretto è 'while (fgets (buff, sizeof (buff), process)) printf ("% s ", buff);' –

3

L'approccio a lungo - che ti dà il controllo completo della stdin, stdout, e stderr del processo figlio, al costo di complessità abbastanza significativo - coinvolge utilizzando fork e execve direttamente.

  1. Prima fork ING, istituito gli endpoint per la comunicazione - pipe funziona bene, o socketpair.Darò per scontato che hai invocato qualcosa come di seguito:

    int childStdin[2], childStdout[2], childStderr[2]; 
    pipe(childStdin); 
    pipe(childStdout); 
    pipe(childStderr); 
    
  2. Dopo fork, nel processo figlio prima execve:

    dup2(childStdin[0], 0); // childStdin read end to fd 0 (stdin) 
    dup2(childStdout[1], 1); // childStdout write end to fd 1 (stdout) 
    dup2(childStderr[1], 2); // childStderr write end to fd 2 (stderr) 
    

    .. quindi chiudere tutte di childStdin, childStdout e childStderr .

  3. Dopo fork, nel processo padre:

    close(childStdin[0]); // parent cannot read from stdin 
    close(childStdout[1]); // parent cannot write to stdout/stderr 
    close(childStderr[1]); 
    

Ora, il processo genitore ha il controllo completo del std i/o del processo figlio - e deve in sicurezza multiplex childStdin[1], childStdout[0], e childStderr[0], monitorando anche per SIGCLD e infine utilizzando una chiamata di serie wait per verificare il codice di terminazione del processo. pselect è particolarmente utile per gestire SIGCLD mentre si gestisce l'I/O std in modo asincrono. Vedi anche select o poll ovviamente.

Se si desidera unire il stdout e stderr, basta dup2(childStdout[1], 2) e di sbarazzarsi di childStderr del tutto infantile.

Le pagine man devono riempire gli spazi vuoti da qui. Quindi è difficile, se ne hai bisogno.

+0

Grazie mille per questa risposta sembra una informazione super utile. Sicuramente lo salverò ed esplorerò in futuro. Grazie per aver risposto così velocemente! – Rhurac

+0

Non si tratta tanto di un ** approccio lungo ** in quanto è più un ** approccio di livello inferiore **. Entrambi hanno i loro posti, Se si tratta senza output da un singolo comando 'popen' fornisce un'interfaccia semplice, ma quando è necessario accedere alle informazioni da parti del codice (più funzioni, ecc ...),' fork, dup, dup2 'fornisce un modo flessibile per soddisfare le vostre esigenze. –

+0

Vero. Se 'popen' risolve il problema, lo risolve con un codice sostanzialmente inferiore. Questo è il modo in cui vai quando 'popen' è insufficiente. – lyngvi

Problemi correlati