2016-12-02 4 views
1

Ich habe eine Menge Probleme mit einem Auftrag für die Schule. Ich sollte Pipes und I/O-Umleitung zu einer Shell-Implementierung in c hinzufügen. Ich habe es bereits mit I/O-Umleitungen arbeiten und die Pipes funktionieren von selbst, aber ich muss etwas wie diese unterstützen "sort < file.txt | grep main | cat> output". Ich habe keine Ahnung, wie ich mit beiden gleichzeitig arbeiten kann. Jede Hilfe würde sehr geschätzt werden.Shell-Implementierung mit Rohren und I O-Umleitung in c

int get_args(char* cmdline, char* args[]){ 
    int i = 0; 

    /* if no args */ 
    if((args[0] = strtok(cmdline, "\n\t ")) == NULL) 
    return 0; 
    while((args[++i] = strtok(NULL, "\n\t ")) != NULL) { 
    if(i >= MAX_ARGS) { 
    printf("Too many arguments!\n"); 
    exit(1); 
    } 
} 
/* the last one is always NULL */ 
return i; 
} 

void pipehandler(char* args[], int nargs){ 

    int num_cmds = 0; 
    char *command[256]; 
    int filedes[2]; // pos. 0 output, pos. 1 input of the pipe 
    int filedes2[2]; 
    int i = 0; 
    //calculate the number of commands 
    for(int i = 0; i < nargs; i++){ 
     if (strcmp(args[i], "|") == 0){ 
      num_cmds++; 
     } 
    } 
    num_cmds++; 
    if(num_cmds <= 1){ 
     return; 
    } 
    int j = 0; 
    int end = 0; 
    pid_t pid; 
    while(args[j] != NULL && end != 1){ 
     int k = 0; 

     while (strcmp(args[j],"|") != 0){ 
      command[k] = args[j]; 
      j++; 
      if (args[j] == NULL){ 
       // 'end' variable used to keep the program from entering 
       // again in the loop when no more arguments are found 
       end = 1; 
       k++; 
       break; 
      } 
      k++; 
     } 
     // Last position of the command will be NULL to indicate that 
     // it is its end when we pass it to the exec function 
     command[k] = NULL; 
     j++; 

    // Depending on whether we are in an iteration or another, we 
    // will set different descriptors for the pipes inputs and 
    // output. This way, a pipe will be shared between each two 
    // iterations, enabling us to connect the inputs and outputs of 
    // the two different commands. 
    if (i % 2 != 0){ 
     pipe(filedes); // for odd i 
    }else{ 
     pipe(filedes2); // for even i 
    } 
    pid=fork(); 

    if(pid==-1){ 
     if (i != num_cmds - 1){ 
      if (i % 2 != 0){ 
       close(filedes[1]); // for odd i 
      }else{ 
       close(filedes2[1]); // for even i 
      } 
     } 
     printf("Child process could not be created\n"); 
     return; 
    } 
    if(pid==0){ 
     // If we are in the first command 
     if (i == 0){ 
      dup2(filedes2[1], STDOUT_FILENO); 
     } 
     // If we are in the last command, depending on whether it 
     // is placed in an odd or even position, we will replace 
     // the standard input for one pipe or another. The standard 
     // output will be untouched because we want to see the 
     // output in the terminal 
     else if (i == num_cmds - 1){ 
      if (num_cmds % 2 != 0){ // for odd number of commands 
       dup2(filedes[0],STDIN_FILENO); 
      }else{ // for even number of commands 
       dup2(filedes2[0],STDIN_FILENO); 
      } 
     // If we are in a command that is in the middle, we will 
     // have to use two pipes, one for input and another for 
     // output. The position is also important in order to choose 
     // which file descriptor corresponds to each input/output 
     }else{ // for odd i 
      if (i % 2 != 0){ 
       dup2(filedes2[0],STDIN_FILENO); 
       dup2(filedes[1],STDOUT_FILENO); 
      }else{ // for even i 
       dup2(filedes[0],STDIN_FILENO); 
       dup2(filedes2[1],STDOUT_FILENO); 
      } 
     } 
     execvp(command[0],command); 
    } 

    // CLOSING DESCRIPTORS ON PARENT 
    if (i == 0){ 
     close(filedes2[1]); 
    } 
    else if (i == num_cmds - 1){ 
     if (num_cmds % 2 != 0){ 
      close(filedes[0]); 
     }else{ 
      close(filedes2[0]); 
     } 
    }else{ 
     if (i % 2 != 0){ 
      close(filedes2[0]); 
      close(filedes[1]); 
     }else{ 
      close(filedes[0]); 
      close(filedes2[1]); 
     } 
    } 

    waitpid(pid,NULL,0); 

    i++; 
    } 



} 

int searchIO(char* args[], int *nargs, char** input_file, char** output_file, int* in, int* out, int* ap){ 
int ioRedirection = -1; 
//search through the array args 
printf("searching for io redirections\n"); 
for(int i = 0; i < *nargs; i++){ 
    if(strcmp(args[i], "<") == 0){ 
     //if you find < then you need to redirect the stdin to be from a file 
     //if you are here then the input file is in args[i+1] 
     *input_file = args[i+1]; 
     printf("this is the input file %s \n", *input_file); 
     //we need to remove the < and input_file from args[] 
     int j; 
     for(j = i; j < *nargs; j++){ 
      //move what is in args[j+2] to args[j]; 
      args[j] = args[j+2]; 
     } 
     //then we need to execute the comand 
     *nargs = *nargs-2; 
     *in = 1; 
     i = 0; 
     ioRedirection = 1; 
    }else if (strcmp(args[i], ">") == 0){ 
     //if you are here then the output file is in args[i+1] 
     //so aparently execute what is in args[0] to args[i] 
     *output_file = args[i+1]; 
     //we need to remove the > and output_file from args[] 
     int j; 
     for(j = i; j < *nargs; j++){ 
      //move what is in args[j+2] to args[j]; 
      if(j < *nargs){ 
       args[j] = args[j+2]; 
      } 
     } 
     *nargs = *nargs-2; 
     *out = 1; 
     i = 0; 
     ioRedirection = 1; 
    }else if (strcmp(args[i], ">>") == 0){ 
     //if you are here then the output file is in args[i+1] 
     //so aparently execute what is in args[0] to args[i] 
     *output_file = args[i+1]; 
     //we need to remove the > and output_file from args[] 
     int j; 
     for(j = i; j < *nargs; j++){ 
      //move what is in args[j+2] to args[j]; 
      args[j] = args[j+2]; 
     } 
     *nargs = *nargs-2; 
     *out = 1; 
     *ap = 1; 
     i = 0; 
     ioRedirection = 1; 
    } 

    } 
    return -1; 

} 

void execute(char* cmdline){ 
int pid, async; 
char* args[MAX_ARGS]; 
char *input_file; 
char *output_file; 
int _in, _out, _ap; 
_in = 0; 
_out = 0; 
_ap = 0; 
int nargs = get_args(cmdline, args); 
pipehandler(args, nargs); 
searchIO(args, &nargs, &input_file, &output_file, &_in, &_out, &_ap); 
if(nargs <= 0) return; 
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) { 
     exit(0); 
} 
if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; } 
else async = 0; 

pid = fork(); 
if(pid == 0) { /* child process */ 
    if(_in == 1){ 
     int input = open(input_file, O_RDONLY); 
     dup2(input, STDIN_FILENO); 
     close(input); 
    } 
    if(_out == 1){ 
     int output; 
     if(_ap){ 
      output = open(output_file, O_APPEND | O_CREAT); 
     }else{ 
      output = open(output_file, O_CREAT | O_WRONLY, 0666); 
     } 

     dup2(output, STDOUT_FILENO); 
     close(output); 
    } 
execvp(args[0], args); 
/* return only when exec fails */ 
    perror("exec failed"); 
    exit(-1); 
    } else if(pid > 0) { /* parent process */ 
if(!async) waitpid(pid, NULL, 0); 
    else printf("this is an async call\n"); 
} else { /* error occurred */ 
perror("fork failed"); 
exit(1); 
} 
} 
int main (int argc, char* argv []) 
{ 
char cmdline[BUFSIZ]; 

    for(;;) { 
    printf("$ "); 
    if(fgets(cmdline, BUFSIZ, stdin) == NULL) { 
    perror("fgets failed"); 
    exit(1); 
    } 
    execute(cmdline) ; 
} 
return 0; 
} 
+0

Keine vollständige Antwort, da dies Hausaufgaben sind, aber Umleitungen mit '|' zu unterstützen, müssen Sie mehr als einmal "fork", verbindet die Ausgangsleitung jedes Programms mit der Eingangsleitung des nächsten. Wenn am Ende ein '>' steht, müssen Sie die Datei öffnen und die Ausgabe der letzten Stufe dorthin umleiten. – Davislor

+0

Werfen Sie einen Blick auf http://stackoverflow.com/questions/3930339/implementing-pipes-in-ac-shell-unix?rq=1 – MrKiwi

+0

Wenn Pipes verwendet werden, ist der einzige Ort, um E/A-Umleitungen zu verwenden erster Befehl und im letzten aber nicht in der Mitte richtig? – tommy

Antwort

0

wenn Rohre der einzige Ort verwendet werden, um E/A-Umleitungen zu verwenden, ist in der erste Befehl und in den letzten ein, aber nicht in der Mitte richtig?

Dies gilt allerdings nur, solange nur stdin/stdout umgeleitet werden.

I/O-Umleitungen und die Rohre selbst arbeiten, aber ... Ich habe keine Ahnung, wie es zugleich mit beiden zu arbeiten.

Umstrukturieren Sie Ihre Implementierung. Es kann nicht funktionieren, wenn Sie

pipehandler(args, nargs); 
searchIO(args, &nargs, &input_file, &output_file, &_in, &_out, &_ap); 

hintereinander nennen, die auch zu dem Fehler führen, dass der erste Befehl einer Pipeline zweimal ausgeführt wird (zuerst von pipehandler, dann von searchIO mit falschen Argumenten). Stattdessen könnten Sie mainpipehandler und dann execute jeden einzelnen Befehl innerhalb pipehandler aufrufen lassen (auch wenn es nur eine und keine Verrohrung gibt). Widersprüchliche E/A-Umleitungen und Pipes könnten leicht diagnostiziert werden.