Prozess starten mit execv



  • Hallo,

    ich möchte einen Prozess starten, wobei Argument und Parameter vom Terminal aus eingelesen werden:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/wait.h>
    
    int main(int argc, char ** argv) {
      char line[100];
      getcwd(line,100);
      printf("%s:",line);
    
      char input[100];
      fgets(input,100,stdin);
    
      char *del = "	 \0";
      char **token;
    
      token = (char**) (malloc(1*sizeof(char *)));
      char *argument = strtok(input,del);//entspricht Argument
      token[0] = strtok(NULL,del); 
      int anz = 0;
      do { //Parameter einlesen
        anz++;
        token = (char**) realloc(token,(anz+1)*sizeof(char *));
        token[anz] = (char*) malloc(100*sizeof(char *));
        token[anz] = strtok(NULL,del);
      }
      while(token[anz]!=NULL);
    
      token[anz] = (char *) NULL;
      //Neuen Prozess erzeugen  
      int cpid;
      cpid = fork();
      execv(argument,token); 
    }
    

    wenn ich das Programm starte und /bin/ls -l -h eingebe, erhalte ich folgende Ausgabe:

    [quote]-l: Ungültige Option --

    -l: Ungültige Option --

    „-l --help“ gibt weitere Informationen.
    „-l --help“ gibt weitere Informationen.

    Was mache ich falsch?
    Danke!


  • Mod

    Der unmittelbare Fehler ist: Du hast noch Whitespace in deinen Funktionsargumenten. Bei mir lautet die genaue Fehlermeldung nämlich zum Beispiel:

    -l: invalid option -- '
    '
    

    Das will mir sagen, dass da noch ein Zeilenumbruch zu viel ist.

    Aber: Sehr viele weitere Fehler:
    - Du hast vermutlich keine Ahnung was fork macht und wieso man es oft zusammen mit exec sieht
    - Dein Programm leakt Speicher wie ein Weltmeister und das liegt nicht nur daran, dass du kein einziges free drin hast, sondern auch am nächsten Punkt:
    - Du kannst mit den Stringfunktionen nicht richtig umgehen und auch nicht mit C-Strings an sich. Indirekt kommt daher auch dein Fehler mit den Whitespaces, denn strtok und operator '=' zusammen machen ganz was anderes als du vermutlich denkst, auch wenn das Ergebnis auf den ersten Blick scheinbar deinen Erwartungen entspricht.
    -Unnötige Casts verbergen eventuelle Fehler. In C sind Zeiger imoplizit in void* umwandelbar und umgekehrt.



  • Danke für deine ausführliche Antwort.

    Was fork() macht, weiß ich tatsächlich noch nicht so genau, das lese ich mir erst durch, wenn der Aufruf von execv funktioniert :).
    Ebenso die free()-Aufrufe, die habe ich noch im Hinterkopf :).

    Nun zu den Strings:
    char* ptr;
    ptr = strtok(string, " ");
    Ich vermute, dass ptr jetzt auf einen String zeigt, der mit \0 abgeschlossen ist und Teilstring von string ist. Was macht es wirklich?
    Kannst du mir noch einen Hinweis geben, wo meine Fehler bzgl. des Umgangs mit Strings liegen?

    Danke!


  • Mod

    qwert_12 schrieb:

    Was fork() macht, weiß ich tatsächlich noch nicht so genau, das lese ich mir erst durch, wenn der Aufruf von execv funktioniert :).

    Doofe Idee. Du musst von jedem Zeichen in deinem Programm genau wissen, wieso du es setzt und was es tut. Einen fetten Systemaufruf mitten im Programm zu haben, bloß weil man mal gesehen hat, dass andere Leute das benutzen, fällt da sicherlich nicht drunter. Mehr über execv und fork erfährst du bei Bedarf im Linuxforum, aber da diese Aufrufe so wichtig sind, solltest du auch genug auf Manpages und in Tutorials lernen können.

    Der Rest deiner Problem sind C, daher lasse ich den Thread auch hier im Forum:

    Ebenso die free()-Aufrufe, die habe ich noch im Hinterkopf :).

    Siehe oben. Programmier gleich richtig, anstatt die Schlamperei anzugewöhnen. Wenn du bewusst fehlerhafte Programme schreibst, brauchst du dich nicht zu wundern, wenn auch andere Teile des Programmes nicht richtig funktionieren. Das passiert nun einmal, wenn man nicht sorgfältig arbeitet.

    Nun zu den Strings:
    char* ptr;
    ptr = strtok(string, " ");
    Ich vermute, dass ptr jetzt auf einen String zeigt, der mit \0 abgeschlossen ist und Teilstring von string ist. Was macht es wirklich?

    Wenn du es so forumulierst, dann ist das genau das was es macht. Aber du benutzt es so, als sollte da irgendetwas kopiert werden oder als würdest du annehmen, dass diese Zeichenkette irgendwie ein neuer, unabhängiger Teil deines Ausgangsstrings wäre. Guck dir mal genau an, was strtok macht und vor allem, was bei den Argumenten const ist und was nicht. Tipp: strtok verändert (zumindest bei der üblichen Implementierung) den Ausgangsstring! Und liefert dann einen Zeiger auf einen Teil dieses veränderten Ausgangsstrings. Wusstest du das schon?

    Kannst du mir noch einen Hinweis geben, wo meine Fehler bzgl. des Umgangs mit Strings liegen?

    Zum Beispiel verwirfst du in Zeile 26 den Wert des in Zeile 25 mit malloc angeforderten Pointers, wohl weil du von irgendeiner Art Kopie ausgehst. Mit '=' kopiert man aber keine Zeichenketten, der malloc-Wert ist nun unwiederbringlich verloren.

    Ich bin mir auch nicht sicher, was das hier soll:

    char *del = "     \0";
    

    Soll das ein Tabulatorzeichen sein? Und was soll die '\0' da?

    Wieso nutzt du in Zeile 25 sizeof(char*), wenn du chars möchtest (sizeof(char) ist übrigens per Definition 1)?



  • Vielen Dank, dass du dir so viel Mühe machst, es mir zu erklären. 👍

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/wait.h>
    
    int main(int argc, char ** argv) {
      char line[100];
      getcwd(line,100);
      printf("%s:",line);
    
      char input[100];
      fgets(input,100,stdin);
    
      char *del = "	 \n";
      char **token;
    
      token = (char**) (malloc(1*sizeof(char *)));
      char *argument = strtok(input,del);//entspricht Argument
      token[0] = strtok(NULL,del); 
      int anz = 0;
      do { //Parameter einlesen
        anz++;
        token = (char**) realloc(token,(anz+1)*sizeof(char *));
        token[anz] = strtok(NULL,del);
      }
      while(token[anz]!=NULL);
    
      token[anz] = (char *) NULL;
      execv(argument,token); 
    
      free(token);
    }
    

    Ist es so besser?
    Das in *del soll ein Tabulator-Zeichen, ein Leerzeichen und ein \n (hab mich verschrieben) sein.


  • Mod

    qwert_12 schrieb:

    Ist es so besser?

    Besser, aber nicht gut. Du hast immer noch die unsinnigen Casts drin.

    Das in *del soll ein Tabulator-Zeichen [...] sein.

    Ist es aber nicht. Probier es mal aus. Tabulator ist '\t'.



  • #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/wait.h>
    
    int main(int argc, char ** argv) {
      char line[100];
      getcwd(line,100);
      printf("%s:",line);
    
      char input[100];
      fgets(input,100,stdin);
    
      char *del = "\t \n";
      char **token;
    
      token = (char**) (malloc(sizeof(char *)));
      char *argument = strtok(input,del);//entspricht Argument
      token[0] = strtok(NULL,del); 
      int anz = 0;
      do { //Parameter einlesen
        anz++;
        token = realloc(token,(anz+1)*sizeof(char *));
        token[anz] = strtok(NULL,del);
      }
      while(token[anz]!=NULL);
    
      token[anz] = NULL;
      execv(argument,token); 
    
      free(token);
    }
    

    Es funktionierte bei mir tatsächlich, dass der Tabulator als Trennzeichen fungiert hat. Liegt wohl an meinem Editor Kate.

    Dann muss es jetzt aber passen. 🙂



  • Ich möchte die Zeile 29 korrigieren zu:

    token[anz] = (char *) NULL;
    

  • Mod

    qwert_12 schrieb:

    Ich möchte die Zeile 29 korrigieren zu:

    token[anz] = (char *) NULL;
    

    Warum? Dann stünde nach Auflösung der Makros (zumindest bei den üblichen Implementierungen von NULL) dort (char*) (void*) 0 . Ziemlich sinnlos, findest du nicht? Korrekturbedarf besteht eher in Zeile 18.



  • Das mit Zeile 29 habe ich gemacht, weil in den man-pages steht:

    The list of arguments must be terminated by a NULL pointer, and, since these are variadic functions, this pointer must be cast (char 😉 NULL.

    Wie ich gerade sehe, gilt dies nur für die Funktionen execl, execle und execlp, nicht aber für execlv. Dann lassen wir das wieder weg :).

    Und Zeile 18 ändere ich zu:

    token = malloc(sizeof(char *));
    

  • Mod

    qwert_12 schrieb:

    Wie ich gerade sehe, gilt dies nur für die Funktionen execl, execle und execlp, nicht aber für execlv. Dann lassen wir das wieder weg :).

    Genau, da wäre das auch richtig. Das jetzt zu erklären würde aber vermutlich für deinen Kenntnisstand zu weit führen. In ein paar Wochen/Monaten sollte das klar sein, wenn du variadische Funktionen und die Typumwandlungsregeln drauf hast. Es wird jedenfalls seinen Grund haben, warum das in den Manpages steht: Viele C-Programmierer kennen diese Regeln vermutlich selber nicht perfekt auswendig und würden vergessen, dass bei dieser Kombination tatsächlich ein Cast notwendig ist.


Anmelden zum Antworten