it-swarm.com.de

N Befehle mit Pipes in einer Shell verbinden?

Ich versuche, eine Shell in C zu implementieren. Ich kann einfache Befehle einfach mit einem einfachen execvp () ausführen, aber eine der Anforderungen besteht darin, Befehle wie folgt zu verwalten: "ls -l | head | tail -4" mit einem 'for 'Schleife und nur eine' Pipe () '- Anweisung, die stdin und stdout umleitet. Nach Tagen bin ich ein bisschen verloren.

N = Anzahl einfacher Befehle (3 im Beispiel: ls, head, tail) -Befehle = eine Liste von Strukturen mit den Befehlen:

commands[0].argv[0]: ls
commands[0].argv[1]: -l
commands[1].argv[0]: head
commands[2].argv[0]: tail
commands[2].argv[1]: -4

Also habe ich die for-Schleife gemacht und angefangen, stdin und stdout umzuleiten, um alle Befehle mit Pipes zu verbinden, aber ... ich bin nur ahnungslos, warum es nicht funktioniert.

for (i=0; i < n; i++){

pipe(pipe);
if(fork()==0){  // CHILD

    close(pipe[0]);
    close(1);
    dup(pipe[1]);
    close(pipe[1]);

    execvp(commands[i].argv[0], &commands[i].argv[0]);
    perror("ERROR: ");
    exit(-1);

}else{      // FATHER

    close(pipe[1]);
    close(0);
    dup(pipe[0]);
    close(pipe[0]);

}
}

Was ich erstellen möchte, ist eine Reihe von untergeordneten Prozessen:

[ls -l] ---- Rohr ----> [Kopf] ---- Rohr ----> [Schwanz -4]

Alle diese Prozesse haben eine Wurzel (der Prozess, der meine Shell ausführt), also ist der erste Vater auch ein Kind des Shell-Prozesses. Ich bin schon ein bisschen erschöpft. Kann mir hier bitte jemand helfen?

Ich bin mir nicht einmal sicher, ob die Kinder die Befehle ausführen sollen.

Danke Leute !!

25
user1031296

Nichts Komplexes hier, denken Sie nur daran, dass der letzte Befehl an den Dateideskriptor 1 des ursprünglichen Prozesses ausgegeben werden sollte und der erste Befehl aus dem ursprünglichen Prozessdateideskriptor 0 lesen sollte vorheriger pipe-Aufruf.

Also hier sind die Typen:

#include <unistd.h>

struct command
{
  const char **argv;
};

Erstellen Sie eine Hilfsfunktion mit einer einfachen, klar definierten Semantik:

int
spawn_proc (int in, int out, struct command *cmd)
{
  pid_t pid;

  if ((pid = fork ()) == 0)
    {
      if (in != 0)
        {
          dup2 (in, 0);
          close (in);
        }

      if (out != 1)
        {
          dup2 (out, 1);
          close (out);
        }

      return execvp (cmd->argv [0], (char * const *)cmd->argv);
    }

  return pid;
}

Und hier ist die Hauptroutine:

int
fork_pipes (int n, struct command *cmd)
{
  int i;
  pid_t pid;
  int in, fd [2];

  /* The first process should get its input from the original file descriptor 0.  */
  in = 0;

  /* Note the loop bound, we spawn here all, but the last stage of the pipeline.  */
  for (i = 0; i < n - 1; ++i)
    {
      pipe (fd);

      /* f [1] is the write end of the pipe, we carry `in` from the prev iteration.  */
      spawn_proc (in, fd [1], cmd + i);

      /* No need for the write end of the pipe, the child will write here.  */
      close (fd [1]);

      /* Keep the read end of the pipe, the next child will read from there.  */
      in = fd [0];
    }

  /* Last stage of the pipeline - set stdin be the read end of the previous pipe
     and output to the original file descriptor 1. */  
  if (in != 0)
    dup2 (in, 0);

  /* Execute the last stage with the current process. */
  return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}

Und ein kleiner Test:

int
main ()
{
  const char *ls[] = { "ls", "-l", 0 };
  const char *awk[] = { "awk", "{print $1}", 0 };
  const char *sort[] = { "sort", 0 };
  const char *uniq[] = { "uniq", 0 };

  struct command cmd [] = { {ls}, {awk}, {sort}, {uniq} };

  return fork_pipes (4, cmd);
}

Scheint zu arbeiten. :)

49
chill

Zunächst schließen Sie die Rohre vorzeitig. Schließen Sie nur das Ende, das Sie im aktuellen Prozess nicht benötigen, und denken Sie daran, stdin/stdout im Kind zu schließen.

Zweitens müssen Sie sich an den Befehl fd des vorherigen Befehls erinnern. Für zwei Prozesse sieht das so aus:

int pipe[2];
pipe(pipe);
if ( fork() == 0 ) {
     /* Redirect output of process into pipe */
     close(stdout);
     close(pipe[0]);
     dup2( pipe[1], stdout );
     execvp(commands[0].argv[0], &commands[0].argv[0]);
} 
if ( fork() == 0 ) {
     /* Redirect input of process out of pipe */
     close(stdin);
     close(pipe[1]);
     dup2( pipe[0], stdin );
     execvp(commands[1].argv[0], &commands[1].argv[0]);
}
/* Main process */
close( pipe[0] );
close( pipe[1] );
waitpid();

Jetzt müssen Sie die Fehlerbehandlung hinzufügen und n-1-Pipes generieren, um n Prozesse zu starten. Der Code im ersten Gabelungsblock () muss für die entsprechende Pipe für die Prozesse 1..n-1 und der Code im zweiten Gabelungsblock () für die Prozesse 2..n ausgeführt werden. 

0
thiton