在C中重定向子进程的文件输出

Posted

技术标签:

【中文标题】在C中重定向子进程的文件输出【英文标题】:Redirecting file output of subprocess in C 【发布时间】:2016-06-17 00:43:06 【问题描述】:

我要么完全忘记了我想知道的关于 C 的一切,要么出现了严重错误。我想将子进程(stdout 和 stderr)的输出重定向到文件。我是这样做的:

if ((pid = fork()) == 0)

   get_host_date(today) ;
   int fd = open(log_filename, O_RDWR | O_CREAT | O_APPEND,
                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
   dup2(fd, STDOUT_FILENO) ;     // make stdout go to file
   dup2(fd, STDERR_FILENO) ;     // make stderr go to file
   fprintf(stderr, "This is a message") ;

   if ((status = execv(command, argv)) == -1)
   
      printf("\n\nError: execve failed for %s", command) ;
      perror("\nexecve failure: ") ;         
      exit(EXIT_FAILURE) ;
   
   close(STDOUT_FILENO) ;
   close(STDERR_FILENO) ;

日志文件按指定创建,但输出未转到该文件。我做了一些测试输出 (fprintf(stderr, "This is a message") ;),但这并没有出现在任何地方。 Wenn 我检查了变量 fd,我看到它的值是 1(紧接在 open 之后)。但是 1 不应该是预定义的输出文件描述符吗?

有人可以帮我吗?我尝试了一切,但没有结果。

非常感谢您 最良好的祝愿 约尔格

P.S.:我正在使用 RHEL 和 GNU-C。

好的,这里有一个不适合我的压缩代码:

int main(int   argc,
         char* argv[])

   PROC_REC    proc ;

   strcpy(proc.command, "/home/islk/entw/v0816/exe/islk_server") ;
   strcpy(proc.args[0], "ISLK_DB1_SERV01") ;
   strcpy(proc.args[1], "") ;
   strcpy(proc.env[0], "ISLKSERVER_NR=5") ;
   strcpy(proc.env[1], "") ;

   ISLK_start_single_process(&proc) ;

   exit(EXIT_SUCCESS) ;


static long ISLK_start_single_process(PROC_REC *prec_ptr)

   long     i=0 ;
   int      status ;
   char     *argv[16] ;
   char     command[256], log_filename[128], today[DB_DATE_DOM] ;
   pid_t    pid ;

   /* Set arguments */
   for (i=0; i<16; i++)
   
      if (strcmp(prec_ptr->args[i], "") != 0)
         argv[i] = prec_ptr->args[i] ;
      else
         argv[i] = NULL ;
   

   /*******************/
   /* Set environment */
   /*******************/
   for (i=0; i<16; i++)
   
      if (strcmp(prec_ptr->env[i], "") != 0)
         putenv(prec_ptr->env[i]) ;
   

   /*****************/
   /* Start process */
   /*****************/
   if ((pid = fork()) == 0)
   
      get_host_date(today) ;
      bs_create_filename(log_filename, "islk$log", "", "%s_%8.8s.log",
                         prec_ptr->args[0], today) ;
      int fd = open(log_filename, O_RDWR | O_CREAT | O_APPEND,
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
      int fdo = dup2(fd, STDOUT_FILENO) ;     // make stdout go to file
      int fde = dup2(fd, STDERR_FILENO) ;     // make stderr go to file
      close(fd) ;    
      fprintf(stdout, "This is a message") ;
      if ((status = execv(command, argv)) == -1)
      
         printf("\n\nError: execv failed for %s", command) ;
         perror("\nexecv failure: ") ;         
         exit(EXIT_FAILURE) ;
      
      close(STDOUT_FILENO) ;
      close(STDERR_FILENO) ;
   
   else if (pid < 0)
   
      printf("\n\nError: fork failed for %s", prec_ptr->args[0]) ;
      perror("\nfork failure: ") ;
      return ERROR ;
   
   else
   
      printf("\nProcess %d started for %s", pid, prec_ptr->args[0]) ;
      prec_ptr->pid = pid ;
   

   return NO_ERROR ;

尝试了以下方法:

dup2(fd, STDERR_FILENO) ;
dup2(fd, STDOUT_FILENO) ;

-> 到 stderr 的消息被写入文件,而不是到 stdout

dup2(fd, STDOUT_FILENO) ;

-> 到 stderr 的消息被写入终端,到 stdout 无处

dup2(fd, STDOUT_FILENO) ;
dup2(STDERR_FILENO, STDOUT_FILENO) ;

-> 到 stderr 的消息被写入文件,到终端上的标准输出

dup2(fd, STDERR_FILENO) ;
dup2(STDOUT_FILENO, STDERR_FILENO) ;

-> 到 stderr 的消息被写入文件,到终端上的标准输出

-> 标准输出似乎有问题!

【问题讨论】:

我无法重现您的问题。请提供minimal reproducible example。如果我添加“明显”位以使您的代码可以干净地编译,它会按预期运行。 也许使用fileno 来确定整数描述符。你也应该在dup2 之后关闭 fd。而且我不确定您是否可以将一个文件用于两个流。 我认为你是对的,fd == 1 是问题的一部分。似乎stdout 之前已被您的进程或其父母之一关闭,并且那里发生的任何事情也可能阻止您的重定向工作(例如dup2(1, 1); 没有意义,但我不知道它是否会伤害) 离题:close() 命令永远不会被执行。如果exec..() 成功,它将永远不会返回,如果失败,您将在调用close()之前exit()ing 总是检查错误。请为每个函数添加适当的检查,并报告您的发现(包括errno) 【参考方案1】:

我认为您几乎已经通过检测fd == 1“在 open() 之后立即”回答了这个问题。根据the dup2() man page:

dup2() 系统调用执行与 dup() 相同的任务,但取而代之的是 使用编号最小的未使用文件描述符,它使用 newfd 中指定的描述符编号。如果描述符 newfd 是 之前打开的,在重新使用之前会静默关闭。

因此,如果 fd == 1 并且您调用 dup2( fd, STDOUT_FILENO ); 则 STDOUT_FILENO(也为 1)关闭,重定向文件的描述符也是如此。如果您无法找出为什么 stdout 在您的程序环境中关闭,也许您可​​以在调用dup2() 之前进行测试:

if( fd != STDOUT_FILENO )
    dup2( fp, STDOUT_FILENO );

至少这应该作为一个技巧来检查我是否正确。

【讨论】:

我发现了为什么 fd == 1(之前关闭了 stdout&stderr)。但我改变了这个(现在 fd == 3 打开后)。到目前为止,我已经正确编写了 stderr。但是到标准输出的消息不会出现在输出文件中,即使我只重定向标准输出(见上文) 可能是一个愚蠢的问题,但你确定exec()d 命令实际上打印了某事。到标准输出?至少你应该在printf() 之后添加一个fflush(stdout) 否则它可能只是在内部缓冲 :我什至在 exec(...) 之前做了一个 fprintf(stdout, ...) 和 fprintf(stderr, ...)。 stderr 进入文件,stdout 没有。明天我会尝试fflush,也许它会有所帮助【参考方案2】:

我现在更加简化了我的程序:

int main(int   argc,
         char* argv[])

   pid_t pid ;

   int fd = open("main.log", O_RDWR | O_CREAT | O_APPEND,
                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
   dup2(fd, STDERR_FILENO) ;     // make stdout go to file
   dup2(fd, STDOUT_FILENO) ;     // make stderr go to file      
   close(fd) ;
   fprintf(stdout, "Main: This is an output message") ;
   fprintf(stderr, "Main: This is an error message") ;
   fflush(stdout) ;
   fflush(stderr) ;

   if ((pid = fork()) == 0)
   
      fd = open("sub.log", O_RDWR | O_CREAT | O_APPEND,
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) ;
      dup2(fd, STDERR_FILENO) ;     // make stdout go to file
      dup2(fd, STDOUT_FILENO) ;     // make stderr go to file      
      close(fd) ;    
      fprintf(stdout, "Sub: This is an output message") ;
      fprintf(stderr, "Sub: This is an error message") ;
      fflush(stdout) ;
      fflush(stderr) ;
      exit(0) ;
   

   exit(EXIT_SUCCESS) ;

这给了我

::::::::::::::
main.log
::::::::::::::
Main: This is an error messageMain: This is an output message
::::::::::::::
sub.log
::::::::::::::
Sub: This is an error messageSub: This is an output message

所以,调用的进程肯定有问题。它输出到标准输出,但是当作为子进程调用时,一定有什么东西阻碍了他这样做(交互调用,标准输出上有很多输出)。 如果有人知道可能是什么原因,我将感谢您提供任何进一步的帮助。 非常感谢您到目前为止的帮助。

约尔格

【讨论】:

【参考方案3】:

尤里卡,我找到了。

我们从外部供应商处调用库函数。在此调用之前,一切正常,之后,不再有输出到 stdout

我现在联系了供应商,询问他们在那里做什么。同时,这也有帮助:

int saved_stdout = dup(STDOUT_FILENO) ;
// vendor library call
dup2(saved_stdout, STDOUT_FILENO) ;

感谢大家的帮助

祝好,周末愉快

约尔格

【讨论】:

以上是关于在C中重定向子进程的文件输出的主要内容,如果未能解决你的问题,请参考以下文章

将子进程的标准输出重定向到 2 个或更多子进程的标准输入

linux中重定向学习总结

使用子进程的python脚本,将所有输出重定向到文件

重定向子进程标准输出

如何获取子进程的输出

Windows:具有重定向输入和输出的子进程