2016-12-02 57 views
1

我在学校的任务中遇到了很多麻烦。我应该在c中将管道和I/O重定向添加到shell实现中。我已经知道它与I/O重定向一起工作,管道可以自己工作,但我需要支持这样的“sort < file.txt | grep main | cat> output”。我不知道如何使它与两个同时工作。任何帮助将不胜感激。用管道和I O重定向在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

由于这是作业,但为了支持使用'|'进行重定向,因此您需要多次fork,将每个程序的输出管道连接到下一个的输入管道。如果最后有'>',则需要打开文件并将最后一个阶段的输出重定向到该文件。 – Davislor

+0

看看http://stackoverflow.com/questions/3930339/implementing-pipes-in-ac-shell-unix?rq=1 – MrKiwi

+0

如果管道正在使用的唯一地方使用I/O重定向是在第一个命令和最后一个,但不是在中间右侧? – tommy

回答

0

如果正在使用的管道使用I/O重定向的唯一地方是在 的第一个命令,并在最后一个,但不是在中间吧?

这仅仅只要仅标准输入/标准输出被重定向如此。

I/O重定向和管道自行工作,但...我不知道 如何使它与两个同时工作。

重构您的实施。如果调用

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

后对方,这也导致该管道的第一个命令(有错误的论点首先从pipehandler,然后从searchIO)执行了两次错误,不能正常工作。相反,您可以让main拨打pipehandler,然后executepipehandler(即使只有一个而没有管道)中的每个单独命令。冲突的I/O重定向和管道很容易被诊断。