如何将输出从管道命令重定向到文件

时间:2013-12-05 01:59:26

标签: c linux shell redirect 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 个答案:

答案 0 :(得分:1)

在我们让输出重定向工作之前,需要修复一个基本错误:在pipe程序中创建一个main,此pipe保持打开状态直到程序结束;从管道读取的每个进程直到EOF都不会在main程序结束之前终止,因此在运行期间输入的管道命令行越多,等待进程就越多(您可以使用{ {1}})。要更正此问题,请在ps之前为管道流程创建pipe;此外,您必须在fork之后关闭管道的两个端(见下文)。

在此之后,输出重定向很简单,类似于 dup2 中的操作,但是您违反了规则:

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

mode 指定在新文件出现时使用的权限   创建。 O_CREAT 时必须提供此参数   在标志;

中指定

因此,管道代码的核心变为:

if normal command has redirection