Shell in C



  • Hallo Zusammen, unterhalb ist ein Gerüst für eine Shell in der Sprache "C". Sie führt jedoch keine Kommandos aus. Kommandos wie "exit" oder "cd" fehlen hier, bzw. meine Kenntnisse sind zu wenig um zu wissen wie man sie hier in C umsetzt. Wäre super wenn ihr mir weiterhelfen könntet.

    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <signal.h>
    
    #define MAX_CMD_LEN 2048
    #define MAX_ARG_NUM 128 
    
    /* Gibt den prompt auf stdout aus
     */
    
    void prompt() {
      printf("\n%s:shelly:] ", getlogin());
      fflush(stdout);
    }
    
    /* Durchsucht die Pfade in der PATH Variablen
     * nach dem Kommando cmd
     */
    
    char* expandCmd(char* cmd) {
      char* path = getenv("PATH");
      char* path_copy = NULL;
      char* delim = ":";
      char* token = NULL;
      struct stat buf;
      char* expandedCmd = NULL; 
      int len = 0;
    
      if(path == NULL || strlen(cmd) == 0)
        return NULL;
    
      path_copy = (char*) malloc((strlen(path) + 1) * sizeof(char));  
      strncpy(path_copy, path,  strlen(path));
      token = strtok(path_copy, delim);   
    
      while(token != NULL) {
        len = strlen(token) + strlen(cmd) + 2; 
        expandedCmd = (char*) malloc(len * sizeof(char));
        memset(expandedCmd,0, len);
        strcat(expandedCmd, token);
        strcat(expandedCmd, "/");
        strcat(expandedCmd, cmd);
    
        if(stat(expandedCmd, &buf) == 0) {
          free(path_copy);
          return expandedCmd;
        }
    
        free(expandedCmd);
        token = strtok(NULL, delim);
      }
    
      free(path_copy);
      return NULL;
    }
    
    /* Einstiegspunkt in die Shell
     */
    
    int main(int argc, char* argv[], char* envp[]) {
    
      //in c_in landen die Zeichen die die Shell entgegennimmt
      char c_in;
    
      //args enthaelt pointer zu den einzelen Argumenten eines eingegebenen
      //Befehls. args[0] enthaelt den Befehl selbst
      char* args[MAX_ARG_NUM];
    
      //in full_cmd wird der gesamte Befehl selbst abgespeichert   
      char* full_cmd = (char*) malloc(MAX_CMD_LEN * sizeof(char));
    
      //in cmd wird der Befehl plus Pfad separat abgelegt
      char* cmd;
    
      //die aktuelle Laenge des Befehls (waehrend der Eingabe in die Shell)
      int curr_cmd_len = 0;
    
      //die Prozess ID des zu erzeugenden Kind-Prozesses
      int chld_pid = 0;
    
      //Zeiger auf die aktuelle Position des zu schreibenden Zeichens 
      //full_cmd
      char* cmd_pnt = full_cmd;
    
      //dezeit zu schreibendes Argument (in args)
      int curr_arg = 0;
    
      //der erste pointer zeigt auf den Anfang von full_cmd (der Befehl 
      //selbst)
      args[0] = full_cmd;
    
      //den String full_cmd mit Nullen fuellen
      memset(full_cmd, 0, MAX_CMD_LEN);
    
      prompt();
    
      //die Shell soll ewig weiterlaufen...
      //in einem Schleifendurchlauf wird ein Zeichen von der Kommandozeile 
      //eingelesen und evtl. die obigen Datenstrukturen angepasst
      while(1) {
    
        //Ueberpruefen, ob noch weitere Zeichen in full_cmd passen
        if(curr_cmd_len >= MAX_CMD_LEN) {
          printf("Command too long.");
          curr_cmd_len = 0;
          args[0] = full_cmd;
          cmd_pnt = full_cmd;
          curr_arg = 0;
          prompt();
          memset(full_cmd, 0, MAX_CMD_LEN);
          continue;
        }
    
        c_in = getchar();
    
        //ein Space bedeutet, dass ein Arguement bzw. der Befehl eingegeben wurde
        if(c_in == ' ') {
          if(curr_cmd_len == 0 || *(cmd_pnt - 1) == ' ')
            continue; 
          //Ueberpruefen, ob args noch weitere Argumente aufnehmen kann,
          //dabei darauf achten das das letzte Argument in args NULL sein muss
          //fuer den execve Aufruf
          if(curr_arg + 1 >= MAX_ARG_NUM - 1) {
            printf("Too many arguments.");
            curr_cmd_len = 0;
    	args[0] = full_cmd;
    	cmd_pnt = full_cmd;
            curr_arg = 0;
            prompt();
            memset(full_cmd, 0, MAX_CMD_LEN);
          } else {	
            *cmd_pnt = '\0';     
            cmd_pnt++;
            curr_arg++;
            curr_cmd_len++;
            args[curr_arg] = cmd_pnt;
          } 
        //falls ein enter eingegeben wurde soll der Befehl ausgefuehrt werden
        } else if(c_in == '\n') {
          args[++curr_arg] = NULL;
    
          cmd = expandCmd(args[0]);
    
          //hier sollte etwas passieren...
    
          prompt();
          curr_cmd_len = 0;
          args[0] = full_cmd;
          cmd_pnt = full_cmd;
          curr_arg = 0;
          memset(full_cmd, 0, MAX_CMD_LEN);
    
          continue;
        } else {
          *cmd_pnt = c_in;
          cmd_pnt++;
          curr_cmd_len++;
        }
      }
    
      return 0;
    }
    

  • Mod

    Was ist deine Frage?



  • Die Shell soll das Kommando cd (change directory)und exit (d.h. die Shell beenden) beinhalten, aber wie schon genannt, meine Kenntnisse sind da ein bischen zu wenig in C, daher würde ich hilfe brauchen.


  • Mod

    Du willst vermutlich noch eine ganze Reihe weiterer solcher "builtin" Kommandos implementieren. Ganz primitiv ist das erst einmal bloss ein Vergleich der Eingabe mit einer Liste der von dir implementierten Kommandos und dann ggf. Aufruf des Kommandos. ~(Wie man exit und cd implementiert ist dir klar, oder? Oder ist es hier, das du Probleme hast? Deswegen habe ich gefragt, was deine Frage ist. Eventuell verschwende ich gerade meine Zeit mit lauter Quatsch, den du gar nicht wissen moechtest 😡 )~
    Sauberer wird es natuerlich, wenn du gleich langfristig denkst. Die builtins muessen logischerweise Prioritaet vor den "normalen" Kommandos haben. Das heisst, diese Liste muss zuerst geprueft werden. ~(Oder hast du Probleme damit, eine Eingabe mit einer Reihe von Kommandos zu vergleichen? Siehe oben: Ohne Frage kann ich bloss spekulieren, was du ueberhaupt moechtest und wenn ich jetzt falsch spekuliere, dann werde ich wenig geneigt sein, nochmal Zeit fuer dich aufzubringen, nachdem du dich doch einmal dazu aufgerafft hast, eine konkrete Frage zu stellen.)~ Langfristig wirst du aber vermutlich:
    1. Mehr builtins einfuegen
    2. Mehr Funktionalitaet in die Shell einbauen wollen. Die populaere Shell bash hat meines Wissens nach ~7 Ebenen an Kommandoprioritaeten, wobei builtins erst irgendwo in der Mitte der Prioritaetsliste auftauchen und "normale" Programmaufrufe ziemlich am Ende. Weil beispielsweise ein alias hoeher zaehlt als ein builtin.
    Wenn du so etwas planst, dann solltest du gleich zusehen, dass dein Code solche Erweiterungen zulaesst.



  • zycon_94 schrieb:

    Die Shell soll das Kommando cd (change directory)und exit (d.h. die Shell beenden) beinhalten,
    aber wie schon genannt, meine Kenntnisse sind da ein bischen zu wenig in C, daher würde ich hilfe brauchen.

    Du vergleichst die Benutzereingabe mit stcmp() ob das erste Wort "exit" oder "cd" ist. Bei exit verlässt
    Du die shell bei cd rufst Du chdir() mit dem zweiten Parameter auf. Die meiste Arbeit macht das Zerlegen und
    interpretieren der Kommandozeile.

    Während exit und cd noch mit ANSI oder POSIX zu lösen ist, wird es bei anderen Kommandos notwendig sein Systemaufrufe zu
    verwenden. Es wäre dann notwendig zu wissen für welches Betriebssystem die Shell funktionieren soll.

    SeppJ schrieb:

    (Wie man exit und cd implementiert ist dir klar, oder? Oder ist es hier, das du Probleme hast?
    Deswegen habe ich gefragt, was deine Frage ist. Eventuell verschwende ich gerade meine Zeit mit lauter Quatsch,
    den du gar nicht wissen moechtest 😡 )

    Berechtigte Frage. Hilfe bei allen Details wie parsen der Kommandos, beim Vergleich, bei der eigentlichen
    Funktionalität oder noch anderswo ?


Anmelden zum Antworten