2016-04-08 20 views
5

Mentre penso di avere la comprensione di come fork(), exec(), wait() e pid funzionano in C, non ho ancora trovato un modo per eseguire un programma personale da un programma.Come eseguire un programma dall'ingresso del terminale (linux)?

Ecco il mio codice:

#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> /* for fork() */ 
#include<sys/types.h> /* for pid_t */ 
#include<sys/wait.h> /* fpr wait() */ 

int main(int argc, char* argv[]) 
{ 
    char fileName[255]; 
    pid_t pid; 

    switch (pid = fork()) { 
    case -1: //Did not fork properly 
     perror("fork"); 
     break; 

    case 0: //child 
     execv(fileName[0],fileName); 

     puts("Oh my. If this prints, execv() must have failed"); 
     exit(EXIT_FAILURE); 
     break; 
    default: //parent 
     //Infinite Loop 
     while (1) { 
      printf(" %s > ", argv[0]); 
      scanf("%s", fileName); // gets filename 
      if (fileName[0] == '\0') continue; 
      printf("\n Entered file: %s",fileName); // prints the fileName 
      waitpid(pid,0,0); /* wait for child to exit() */ 
      break; 
     } 
    } 

    return 0; 
} 

Le mie domande sono le seguenti:

  1. voglio prendere una stringa come input e voglio limitare il campo di applicazione a 255 caratteri. char fileName[255] e poi scanf("%s", fileName); la strada da percorrere? Dovrei usare getLine() o qualche altra funzione?

  2. Diciamo che l'input è preso correttamente. Come posso dire di dire un programma Hello Hello esistente. L'input verrà archiviato in *argv[]? Ho scoperto che in un programma diverso potrei usare

    static char *argv[] = { "echo", "Foo is my name." , NULL }; 
    execv("/bin/echo", argv); 
    

    per echo "Foo è il mio nome". Posso fare qualcosa di simile con un programma helloWorld?

risposta

2

si sta passando un singolo carattere, come il nome del comando, e la stringa nome come l'inizio di una lista di argomenti - come se il prototipo per execv() erano int execv(char cmd, char *args).

Il prototipo attuale è: int execv(char *cmd, char **args), quindi è necessario:

char *args[2] = { fileName, 0 }; 

execv(args[0], args); 

presumo si imposta fileName ad un valore significativo da qualche parte - non viene mostrato. Ad esempio, potrebbe essere "./local_program". Sarà trattato come il percorso del file eseguibile.

Se si vuole leggere il nome, quindi è possibile utilizzare fgets() o getline(), ma sarà necessario rimuovere il ritorno a capo:

if (fgets(fileName, sizeof(fileName), stdin) != 0) 
{ 
    fileName[strcspn(fileName, "\n")] = '\0'; 
    …as before… 
} 

o:

char *fileName = 0; 
size_t length = 0; 
if (getline(&fileName, &length, stdin) != -1) /* Not EOF! */ 
{ 
    fileName[strcspn(fileName, "\n")] = '\0'; 
    …as before… 
} 
free(fileName); 

L'uso di strcspn() evita di dover utilizzare righe di comando troppo lunghe per casi particolari, in modo che non ci sia una nuova riga nello fileName. Si noti che questo non tenta di dividere la riga di input in un nome di comando e argomenti a spazi o qualcosa di simile. Questo è il successivo livello di attuazione shell:

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

int main(void) 
{ 
    char fileName[256]; 
    if (fgets(fileName, sizeof(fileName), stdin) != 0) 
    { 
     fileName[strcspn(fileName, "\n")] = '\0'; 
     char *args[129]; 
     char **argv = args; 
     char *cmd = fileName; 
     const char *whisp = " \t\f\r\b\n"; 
     /* Ugh — strtok()? OK while only handling white space separators */ 
     char *token; 
     while ((token = strtok(cmd, whisp)) != 0) 
     { 
      *argv++ = token; 
      cmd = 0; 
     } 
     *argv = 0; 

     execv(args[0], args); 
     fprintf(stderr, "Oops!\n"); 
    } 
    return 1; 
} 

non ho bisogno di controlli di overflow della matrice args perché 256 caratteri di input, meno terminazione nullo, non possono essere divisi per produrre più di 128 argomenti solo carattere , ciascuno separato da quelli adiacenti da un singolo carattere di spazio bianco. L'utilizzo di strtok() è un cerotto temporaneo. Non appena si ha a che fare con la sintassi della shell reale (pipe, reindirizzamenti I/O, stringhe tra virgolette con spazi, ecc.), Lo strumento strtok() è tristemente sbagliato. (It - strtok() - è anche la funzione sbagliata da utilizzare in qualsiasi funzione di libreria di sorta.. Utilizzare POSIX strtok_r() su Unix o Microsoft di strtok_s() su Windows se è necessario utilizzare una funzione strtok() -come nel codice della libreria)

+1

Grazie per la vostra risposta rapida ed esauriente. L'unica parte della tua domanda che non riuscivo a capire completamente era il presupposto che impostassi fileName su un valore. Intendi inizializzazione? In tal caso, non l'ho fatto. Ho bisogno di? Nella mia mente, quando il programma girava, chiedeva all'utente e l'utente digita qualcosa come "./helloWorld" o "/bin/./helloWorld". E questo sarebbe il valore di fileName. –

+1

La digitazione del nome è ciò che intendevo impostando 'fileName' su un valore '. Come scritto, si stava usando una stringa di caratteri non inizializzata come comando, che è improbabile che funzioni bene. Finché imposti 'fileName' su qualcosa di leggermente appropriato in qualche modo, dovresti essere OK. Sia './HelloWorld' e'/bin /./ helloWorld' funzionerebbero, anche se '. /' Non è necessario ('execv()' non è influenzato da '$ PATH', e se si specifica solo' helloWorld', sarà trattato come './helloWorld'). '/ Bin /./ helloWorld' è iffy; il '/./' potrebbe essere solo '/' e non si dovrebbe mettere il software in '/ bin'. –

0

Così com'è, è necessario correggere gli errori di compilazione dal codice:

g++ -std=c++11 -g -Wall -Wextra -Wwrite-strings  36501711.cpp -o 36501711 
36501711.cpp: In function ‘int main(int, char**)’: 
36501711.cpp:18:25: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive] 
     execv(fileName[0],fileName); 
         ^
36501711.cpp:18:35: error: cannot convert ‘char*’ to ‘char* const*’ for argument ‘2’ to ‘int execv(const char*, char* const*)’ 
     execv(fileName[0],fileName); 
           ^
36501711.cpp: At global scope: 
36501711.cpp:7:5: warning: unused parameter ‘argc’ [-Wunused-parameter] 
int main(int argc, char* argv[]) 
    ^

L'avviso (circa argc non utilizzato) è innocuo, ma gli errori sono reali e devono essere corretti.

è necessario inizializzare fileNameprima si forchetta, altrimenti il ​​bambino non avrà alcun dato valido lì:

#include<stdio.h> 
#include<stdlib.h> 
#include<unistd.h> /* for fork() */ 
#include<sys/types.h> /* for pid_t */ 
#include<sys/wait.h> /* fpr wait() */ 

int main(int argc, char* argv[]) 
{ 
    char fileName[255]; 
    pid_t pid; 

    //Infinite Loop 
    while (1) { 
     printf(" %s > ", argv[0]); 
     scanf("%s", fileName); 
     if (fileName[0] == '\0') 
      break; 
     printf("\n Entered file: %s\n", fileName); 

     pid = fork(); 
     switch (pid) { 
     case -1: //Did not fork properly 
      perror("fork"); 
      break; 

     case 0: //child 
      execl(fileName, fileName, 0); 
      perror("exec"); 
      exit(EXIT_FAILURE); 
      break; 

     default: //parent 
      waitpid(pid,0,0); /* wait for child to exit() */ 
      break; 
     } 
    } 

    return EXIT_SUCCESS; 
} 
Problemi correlati