Как перенаправить вывод из команды pipe в файл

У меня есть программа, которая анализирует данную команду и распределяет все аргументы / программы в структуре. В моей основной программе, которая выполняет команды, я пытаюсь перенаправить вывод команды pipe в файл, если задано ">". Например, моя программа успешно выполнит команду

cat filea | grep pattern

но я также хочу иметь возможность выполнить команду

cat filea | grep pattern > outfile

В качестве примечания, не слишком важно понимать точную механику cmdscan.c, поскольку он был задан как своего рода вспомогательная программа, помогающая разобрать строку команды и заполнить значения структуры, что упрощает проверку случаев в основная программа hsh.c. Кроме того, argv1 и argv2 - это левая и правая части трубы, поэтому argv2 заполняется только при наличии трубы. И если есть перенаправление любого вида, то имя файла будет сохранено в infile/outfile в зависимости от перенаправления

Это моя основная программа hsh.c, которая выполняет команды:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <string.h>

#define BUFSIZE 500
struct cmd
  {
    int redirect_in;     /* Any stdin redirection?         */
    int redirect_out;    /* Any stdout redirection?        */
    int redirect_append; /* Append stdout redirection?     */
    int background;      /* Put process in background?     */
    int piping;          /* Pipe prog1 into prog2?         */
    char *infile;        /* Name of stdin redirect file    */
    char *outfile;       /* Name of stdout redirect file   */
    char *argv1[10];     /* First program to execute       */
    char *argv2[10];     /* Second program in pipe         */
  };

int cmdscan(char *cmdbuf, struct cmd *com);

int main() {
    char buf[BUFSIZE];
    struct cmd command;
    pid_t pid;
    int status;
    int fd[2];
    pipe(fd);
    int fdout;

    while((fgets(buf,BUFSIZE,stdin) != NULL)) {

    if(cmdscan(buf,&command)==-1) {
        printf("illegal format\n"); 
        continue; 
    }


    if((pid=fork()) <0) 
        perror("fork error\n"); 

    if(strcmp(command.argv1[0],"exit") == 0) {
            return 0; 
        }
    else if (pid == 0) {
        //if the command has piping
        if(command.piping){
            if((pid = fork()) <0)
                perror("fork error");
            //fork again so we can do more commands after this one
            else if(pid == 0) {
                if((pid = fork()) < 0)
                    perror("fork error");
                else if (pid == 0){
                    //fdout = open(command.outfile, O_CREAT | O_WRONLY);
                    //dup2(fdout, STDOUT_FILENO);
                    dup2(fd[1], STDOUT_FILENO);
                        close(fd[1]);
                        execvp(*command.argv1,command.argv1);


                } else {
                    dup2(fd[0],STDIN_FILENO);
                    close(fd[0]);
                    execvp(*command.argv2,command.argv2);
                }
            }
        //execute normal command
        }else {
            //if normal command has redirection
            if(command.redirect_out){
                fdout = open(command.outfile, O_CREAT | O_WRONLY);
                dup2(fdout,STDOUT_FILENO);
                close(fd[0]);
                execvp(*command.argv1,command.argv1);

            }
            else{
                execvp(*command.argv1,command.argv1);
            }
        }
        //..
        exit(0);
    } else {
        if(wait(&status)!=pid)
            perror("wait error"); 
    }
     }


    return 0;
}

Это программа, которая анализирует командную строку, cmdscan.c.:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

struct cmd
  {
    int redirect_in;     /* Any stdin redirection?         */
    int redirect_out;    /* Any stdout redirection?        */
    int redirect_append; /* Append stdout redirection?     */
    int background;      /* Put process in background?     */
    int piping;          /* Pipe prog1 into prog2?         */
    char *infile;        /* Name of stdin redirect file    */
    char *outfile;       /* Name of stdout redirect file   */
    char *argv1[10];     /* First program to execute       */
    char *argv2[10];     /* Second program in pipe         */
  };

#define TRUE 1
#define FALSE 0

int 
cmdscan(char *cmdbuf, struct cmd *com)
{
  char *token;
  char *curcmd;        /* pointer to current command string */
  char swtch[256];    /* buffer to hold copy of switch */
  char *separators = " \t\n";
  int i;

  com->redirect_in = FALSE;
  com->redirect_out = FALSE;
  com->redirect_append = FALSE;
  com->background = FALSE;
  com->piping = FALSE;

  if ( (com->argv1[0] = strtok(cmdbuf,separators) ) == NULL)
    return(-1);
  i = 1;
  while( (com->argv1[i++] = (token = strtok(NULL,separators))) != NULL && strcmp(token,">") &&
        strcmp(token,"<") && strcmp(token,">>") && strcmp(token,"&") && strcmp(token,"|") );
  com->argv1[i-1] = NULL;

  if ( token != NULL && strcmp(token,"|") == 0 )
    {
      com->piping = TRUE;
      i = 0;
      while( (com->argv2[i++] = (token = strtok(NULL,separators))) != NULL && strcmp(token,">") &&
        strcmp(token,"<") && strcmp(token,">>") && strcmp(token,"&") && strcmp(token,"|") );
      com->argv2[i-1] = NULL;
      if ( com->argv2[0] == NULL )
        return(-1);
    }

  while ( token != NULL ){

    if ( !strcmp(token,">") || !strcmp(token,">>") )
      {
        if ( com->redirect_out )
          return(-1);
        com->redirect_out = TRUE;
        if ( !strcmp(token,">>") )
          com->redirect_append = TRUE;
        if ( (com->outfile = strtok(NULL,separators)) == NULL )
          return(-1);
      } 
    else if ( !strcmp(token,"<") )
      {
        if ( com->redirect_in )
          return(-1);
        com->redirect_in = TRUE;
        if ( (com->infile = strtok(NULL,separators)) == NULL )
          return(-1);
      } 
    else if ( !strcmp(token,"|") )
      {
        if ( com->piping )
          return(-1);
      } 
    else if ( !strcmp(token,"&") )
      {
        if ( com->background )
          return(-1);
        com->background = TRUE;
        if ( (token = strtok(NULL,separators)) != NULL )
          return(-1);
        break;
      }
    else
      return(-1);

    token = strtok(NULL,separators);
  }

  return(0);
}

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

1 ответ

Прежде чем мы получим работу перенаправления вывода, необходимо исправить одну основную ошибку: pipe в main программа, и это pipe остается открытым до конца программы; каждый процесс, который читает из канала, пока EOF не завершится до конца main программа, поэтому, чем больше командных строк конвейера вы введете во время выполнения, тем больше будет процессов ожидания (вы можете наблюдать это с ps). Чтобы исправить это, создайте pipe как раз перед fork для трубопроводных процессов; кроме того, вы должны закрыть оба конца трубы после dup2 (увидеть ниже).

После этого перенаправление вывода простое, аналогичное тому, что вы сделали в случае if normal command has redirection, но вы нарушили правило:

 int open (const char * pathname, int flags, mode_t mode); 

mode определяет разрешения для использования в случае создания нового файла. Этот аргумент должен быть указан, когда в флагах указан O_CREAT;

Таким образом, ядром вашего кода становится:

                pipe(fd);
                if ((pid = fork()) < 0)
                    perror("fork error");
                else if (pid == 0) {
                    dup2(fd[1], STDOUT_FILENO);
                    close(fd[0]);   // close both fd in child
                    close(fd[1]);
                    execvp(*command.argv1, command.argv1);
                } else {
                    dup2(fd[0], STDIN_FILENO);
                    close(fd[0]);
                    close(fd[1]);   // close both fd in parent
                    if (command.redirect_out)
                    {
                        fdout =
                          open(command.outfile, O_CREAT|O_WRONLY|O_TRUNC, 0666);
                        dup2(fdout, STDOUT_FILENO);
                    }
                    execvp(*command.argv2, command.argv2);
                }
Другие вопросы по тегам