2016-12-02 3 views
1

У меня много проблем с заданием для школы. Я должен добавить каналы и перенаправление ввода-вывода к реализации оболочки в c. Я уже получил его для работы с перенаправлением ввода-вывода, и сами трубы работают, но мне нужно поддерживать что-то вроде этого «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

, если используются трубы, единственное место для использования перенаправления ввода-вывода находится в первая команда и в последнем, но не в середине справа? – tommy

ответ

0

если трубы используются единственное место для использования переназначения ввода/вывода в первой команды и в последнем, но не в середине справа?

Это верно лишь до тех пор, перенаправляются только STDIN/стандартный вывод.

переназначения ввода/вывода и трубы работают сами по себе, но ... Я понятия не имею, , как заставить его работать с обоими одновременно.

Реконструкция вашей реализации. Он не может работать, если вы звоните

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

друг за другом, что также приводит к ошибке, что первая команда трубопровода выполняется дважды (первый из pipehandler, то из searchIO с неправильными аргументами). Вместо этого вы можете позволить main позвонить pipehandler, а затем execute каждой отдельной команде из pipehandler (даже если есть только один и без трубопроводов). Противоречивые перенаправления и протоколы ввода/вывода могут быть легко диагностированы.