2013-03-21 9 views
8

Sto provando a passare gli argomenti inseriti dall'utente a execvp().Passare un array a execvp() dall'input dell'utente

Finora ho diviso la stringa. Se l'utente digita ls -a, temp viene salvato come "ls" e "-a" seguito da un carattere NULL. Non sono abbastanza sicuro su come farlo correttamente in execvp. Negli esempi l'ho visto usando execvp(temp[position], temp). So che il modo in cui sto provando a farlo al momento è sbagliato, ma non sono sicuro di come farlo correttamente! Al momento sto ricevendo un errore di segmentazione.

int main(int argc, char *argv[]) 
{ 
    char line[124]; 
    int size = 124; 
    char *temp = NULL; 

    while(fgets(line, size, stdin) != NULL) { 
     if (strcmp(line, "exit\n") == 0) { 
      exit(EXIT_SUCCESS); 
     } 
     temp = strtok(line, " "); 
     while (temp != NULL) { 
      printf("%s\n", temp); 
      temp = strtok(NULL, " "); 
     } 
     execvp(temp, &temp);  
    } 
    return EXIT_SUCCESS; 
} 
+2

Dato che non si sta usando 'argc' o' argv', si potrebbe usare 'int main (void)'. Con le opzioni del compilatore io uso abitualmente, che impedisce un paio di avvertimenti. –

+0

@ JonathanLeffler Le userò più tardi. Solo non in questo momento. – caerulean

+1

Abbastanza onesto - ma per un SSCCE ([Esempio breve, autonomo e corretto] (http://sscce.org/)), ti sforzi di eliminare tutto ciò che è irrilevante nell'esempio ridotto. È un problema minore - molto minore. La maggior parte dei programmi offre molte più possibilità di cecchinaggio rispetto alla tua. –

risposta

6

Il tuo problema è che temp è un puntatore singolo ed è necessario passare una serie di puntatori a execvp().

Qualcosa di simile:

enum { MAX_ARGS = 64 }; 
    char *args[MAX_ARGS]; 
    char **next = args; 

    temp = strtok(line, " "); 
    while (temp != NULL) 
    { 
     *next++ = temp; 
     printf("%s\n", temp); 
     temp = strtok(NULL, " "); 
    } 
    *next = NULL; 
    execvp(args[0], args); 

Nota che la lista degli argomenti è stato dato un puntatore nullo come terminatore, proprio come argv[argc] == NULL in main(). Chiaramente, ho risparmiato sul controllo degli errori (se si superano più di 63 argomenti, si verificherà un overflow della matrice args). Ma questo contiene l'idea centrale.


Con questo esempio, io non riesco a ottenere il semplice comando del ls al lavoro, ho provato mkdir e echo e sembrano funzionare bene. Passando a ls restituisce -1 da execvp().

io non sono sicuro di quello che il problema potrebbe essere - tutto questo lavoro per me:

  • ls
  • ls -l
  • ls -l madump.c (dove madump.c capita di essere un file nella directory Sto provando)

Il codice che ho usato era:

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

int main(void) 
{ 
    char line[1024]; 

    while (fgets(line, sizeof(line), stdin) != NULL) 
    { 
     if (strcmp(line, "exit\n") == 0) 
      exit(EXIT_SUCCESS); 

     char *args[64]; 
     char **next = args; 
     char *temp = strtok(line, " \n"); 
     while (temp != NULL) 
     { 
      *next++ = temp; 
      printf("%s\n", temp); 
      temp = strtok(NULL, " \n"); 
     } 
     *next = NULL; 

     puts("Checking:"); 
     for (next = args; *next != 0; next++) 
      puts(*next); 

     execvp(args[0], args); 
    } 

    return EXIT_SUCCESS; 
} 

Nota che ho aggiunto \n alla strtok() lista di token, dopo aver creato una directory con un nuova riga alla fine del suo nome. Ottimo per gli amici fastidiosi e i nemici sconcertanti semi-istruiti, ma una seccatura dalla maggior parte delle altre prospettive. Nota come stampo i dati che verranno passati a execvp() poco prima di farlo davvero. Spesso, userei printf("<<%s>>\n", *next); anziché solo puts() per ottenere un'indicazione univoca di dove iniziano e terminano gli argomenti.

L'output di eseguire il comando (doit) era:

$ ./doit 
ls -l madump.c 
ls 
-l 
madump.c 
Checking: 
ls 
-l 
madump.c 
-rw-r--r-- 1 jleffler staff 2352 Jul 28 2011 madump.c 
$ 

Cosa hai preso dalla versione?

+0

Non dovrei aggiungere anche ciò che è in temp to args prima di chiamare execvp? – caerulean

+1

Quello (aggiungendo ciò che è in 'temp' a' args') viene fatto tramite 'next'. Inizialmente, '* next' punta a' args [0] '; il valore in 'temp' viene copiato in' args [0] 'e' next' viene incrementato in modo da puntare a 'args [1]'; risciacqua e ripeti. Per inciso, normalmente ci sarebbe anche un 'fork()' nel codice, altrimenti il ​​programma si fermerà quando eseguirà con successo un comando. –

+0

Per ora ho mantenuto il mio array di args alla dimensione di 16. Dopo averlo reso funzionante, implementerò fork() in modo da poter gestire più processi. – caerulean

1

Sto supponendo che il segfault è perché si passa un puntatore NULL a execvp. Il ciclo immediatamente sopra la chiamata lo assicura.

Per fare ciò che si sta tentando di fare, è necessario creare una serie di puntatori di stringa per ciò che attualmente si chiama temp. Questo diventa l'array argv per il programma che si chiama. Ecco perché il comando viene solitamente utilizzato come execvp (temp[0], temp) - argv[0] è solitamente il nome del programma.

Quindi provare a creare una matrice di puntatori a stringa e ogni punto a una parola con token da line. Potrebbe essere necessario utilizzare malloc, anche se se vuoi essere intelligente potresti essere in grado di puntare direttamente su line. Se lo fai, dovrai impostare il carattere immediatamente dopo ogni "parola" su \0.

2

Come sembra attualmente il tuo codice, dopo le tue finiture while(temp != NULL), temp sarà NULL!

execvp si aspetta che l'argomento primo sia il percorso del file che sarà la nuova immagine di processo. Si prevede che l'argomento secondo sia un array di stringhe terminate NULL in cui l'ultimo membro di tale array è un puntatore NULL e il primo membro è il nome file del file specificato nel primo argomento.

Per attuare questo nel codice, si consideri il while ciclo seguente invece:

char **argList = NULL; 
unsigned int numArgs = 0; 
while (temp != NULL) { 
    numArgs++; 

    /* Reallocate space for your argument list */ 
    argList = realloc(argList, numArgs * sizeof(*argList)); 

    /* Copy the current argument */ 
    argList[numArgs - 1] = malloc(strlen(temp) + 1, 1); /* The +1 for length is for the terminating '\0' character */ 
    snprintf(argList[numArgs - 1], strlen(temp) + 1, "%s", temp); 

    printf("%s\n", temp); 
    temp = strtok(NULL, " "); 
} 

/* Store the last NULL pointer */ 
numArgs++; 
argList = realloc(argList, numArgs * sizeof(*argList)); 
argList[numArgs - 1] = NULL; 

/* Finally, pass this to execvp */ 
execvp(argList[0], argList); 
/* If you reach here, execvp() failed */ 

Il codice che ho fornito sopra non fare un po 'di controllo degli errori (ad esempio quando realloc o malloc fallisce), ma fondamentalmente tenere questi punti in mente:

  1. Argomento 1: nome percorso del file che sta per essere messo in memoria.
  2. Argomento 2: Un elenco di argomenti in cui primo membro di tale elenco = nome file, ultimo membro = puntatore NULL.

Si prega di guardare the documentation per una migliore chiarezza e un esempio molto semplice.

+0

Non è necessario copiare le stringhe perché 'temp' punta a parti della serie' line'. Se copi le stringhe, ti consiglio di usare 'strdup()'. 'Snprintf()' è interessante. L'esplicito '\ 0' è davvero superfluo; comunque alla fine ci sarà ancora un null. L'ultimo 'sprintf()' non fa quello che pensi che faccia. Si blocca o scrive una stringa come '(null)' nella variabile in cui hai solo assegnato abbastanza spazio per un ''\ 0''. –

+0

Oh wow non avevo idea di 'strdup()'. Sembra invece molto più pulito da usare. Oh yeah 'snprintf()' copia il '\ 0', il mio male. Non ho capito il tuo ultimo punto però. In realtà, mi sono appena reso conto che 'strlen ((char *) 0)' = crash. '(char *) 0' =' (null) 'quindi sono un po 'incapace di allocare memoria per quello. Eventuali suggerimenti? –

+0

Usa: 'argList [numArgs - 1] = NULL;' (o '0' al posto di' NULL') invece del finale post-ciclo 'sprintf()'. L'ultimo valore nell'array deve essere un puntatore nullo. In teoria, dovresti controllare che anche le allocazioni di memoria abbiano successo. Inoltre, sebbene renda il codice un po 'più difficile, è una buona idea allocare la memoria per 'argList' in blocchi (diciamo 64 puntatori alla volta), per evitare il comportamento quadratico man mano che si aumenta il blocco di una voce alla volta . Tuttavia, questa è una raffinatezza per dopo. –

Problemi correlati