c popen 不会捕获 stderr

问题描述 投票:0回答:4

我正在尝试使用

popen()
来捕获调用的 stderr,但当然它似乎并没有这样做。有什么想法吗?

我的代码看起来或多或少像这样:

popen("nedit", "r");

但是我的屏幕上出现了所有关于非 utf8 的垃圾...

c popen stderr
4个回答
48
投票

popen
为您提供进程标准输出的文件句柄,而不是其标准错误。它的第一个参数被解释为 shell 命令,因此您可以在其中进行重定向:

FILE *p = popen("prog 2>&1", "r");

或者,如果您根本不需要标准输出,

FILE *p = popen("prog 2>&1 >/dev/null", "r");

(除了

/dev/null
之外的任何其他文件也可以。)


5
投票

如果你想放弃所有的错误消息,那么你可以使用:

popen("nedit 2>/dev/null", "r");

2
投票

如果您同时需要 stdout 和 stderr 流,您可以打开一个额外的管道并将子 stderr 重定向到它:

if (pipe(pfd) < 0)
    return -1;
    
perr = fdopen(pfd[0], "r");

snprintf(command, LINE_LEN, "... 2>&%d", pfd[1]);

if (pout = popen(command, "r")) {
    close(pfd[1]);

    while (fgets(line, LINE_LEN, pout) != NULL)
        ...
    
    while (fgets(line, LINE_LEN, perr) != NULL)
        ...

    pclose(pout);
}

fclose(perr);
close(pfd[0]), close(pfd[1]);

正如 Andrew 指出的,如果

command
生成大量输出/错误,则必须更仔细地处理流(请参阅下面的评论)。


0
投票

Andre Luis da Silva Monteiro 的解决方案非常出色。 只是为了提供额外的选择,我添加了下面的提案。取代了 popen(),而是创建了一个新进程,提供所有连接管道 - stdin、stdout 和 stderr。 Andrew Henle 的评论也适用。


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

/* These definitions are for issuing error messages.
 */
#define WarningMessage( ErrMess) fprintf(stderr, "%s() (file %s, line %d): Warning: %s\n", \
    __func__, __FILE__, __LINE__, (ErrMess) )
#define ErrorMessage( ErrMess) fprintf(stderr, "%s() (file %s, line %d): Error: %s\n", \
    __func__, __FILE__, __LINE__, (ErrMess) )
#define ErrorMessageFromCall(Func) fprintf(stderr, "%s() (file %s, line %d): Error from function %s(): Errno: (%d) [%s]\n", \
    __func__, __FILE__, __LINE__, (Func), errno, strerror(errno))
#define ErrorMessageFromCall1(Func) fprintf(stderr, "%s() (file %s, line %d): Error from function %s()\n", \
    __func__, __FILE__, __LINE__, (Func))

#define buffLen 1024
#define MAX_EVENTS 10


int main()
{
  int pip0[2], pip1[2], pip2[2];
  int FDChildStdin, FDChildStdout, FDChildStderr;
  int iret, i;
  pid_t PID, PID1;
/* This is the command to execute and its arguments. See execve() for more details */  
  char Command[]="/usr/bin/ls";
  char *argv[3]={ [0]="ls", "-l", };
  char *envp[1]={};
  
  char buff[buffLen];
  int FDEpollInst;
  struct epoll_event ev, events[MAX_EVENTS];
  int nfds;

  iret= pipe( pip0);
  if( iret)
  {
    ErrorMessageFromCall( "pipe()");
    return -1;
  }
  iret= pipe( pip1);
  if( iret)
  {
    ErrorMessageFromCall( "pipe()");
    return -1;
  }
  iret= pipe( pip2);
  if( iret)
  {
    ErrorMessageFromCall( "pipe()");
    return -1;
  }

  PID= fork();
  if( PID < 0)
  {
    ErrorMessageFromCall("fork()");
    return -2;
  }
  if( PID == 0)
  {
/* here we are in the child process  
   This child process will be reading from pip0, writing to pip2 and pip3. 
   * This will be the new stdin, stdout and stderr.
   * The process should not know about the previous file descriptors. */     
    close( pip0[1]);
    close( pip1[0]);
    close( pip2[0]);
    
    iret= dup2(pip2[1], 2);
    if( iret == -1)
    { /* This will be probably the last message written to the original stderr from this process... */
        ErrorMessageFromCall( "In child process: pipe()");
        return -1;
    }
    iret= dup2(pip1[1], 1);
    if( iret == -1)
    { /* This will already go to the stderr pipe. See the previous call. */
        ErrorMessageFromCall( "pipe()");
        return -1;
    }
    iret= dup2(pip0[0], 0);
    if( iret == -1)
    { 
        ErrorMessageFromCall( "pipe()");
        return -1;
    }
    close( pip0[0]);
    close( pip1[1]);
    close( pip2[1]);

    iret= execve(Command, argv, envp);
    if( iret == -1)
    { 
        ErrorMessageFromCall( "execve()");
        return -1;
    } 
/* Now, the Command runs in this process. No more control in this code. */
    
  } else
  {
/* here we are in the parent process  */
/* The child process will be writing to pip0, reading from pip2 and pip3.
 * This process will use the oposite ends of the pipes. */     
    FDChildStdin= pip0[1];
    FDChildStdout= pip1[0];
    FDChildStderr= pip2[0];

   FDEpollInst= epoll_create1(0);
   if (FDEpollInst == -1) 
   {
       ErrorMessageFromCall( "epoll_create1()");
       return -2;
   }

   ev.events = EPOLLIN;
   ev.data.fd = FDChildStdout;
   if(epoll_ctl(FDEpollInst, EPOLL_CTL_ADD, FDChildStdout, &ev) == -1) 
   {
       ErrorMessageFromCall( "epoll_ctl()");
       return -2;
   }
   ev.events = EPOLLIN;
   ev.data.fd = FDChildStderr;
   if(epoll_ctl(FDEpollInst, EPOLL_CTL_ADD, FDChildStderr, &ev) == -1) 
   {
       ErrorMessageFromCall( "epoll_ctl()");
       return -2;
   }

   for( ;; )
   {
/* Wait for data from child. Timeout 10 seconds. 
 * Would be better to decrease timeout in subsequent calls,
 * if loop is repeated because of interrupt (errno == EINTR). */
       nfds = epoll_wait(FDEpollInst, events, MAX_EVENTS, 10000);
       if( (nfds == -1) && (errno != EINTR))
       {
           ErrorMessageFromCall("epoll_wait()");
           return -2;   
       } 
       if( nfds > -1) break;
   }
   if( nfds == 0)
   {
       printf( "No data from child available within the timeout.\n");
   }
   for( i= 0; i< nfds; i++)
   {
     if( events[i].events & EPOLLIN) printf( "file is available for read(2) operations.\n");
     if( events[i].events & EPOLLOUT) printf( "file is available for write(2) operations.\n");
     if( events[i].events & EPOLLRDHUP) printf( "Stream socket peer closed connection, or shut down writing half of connection.\n");
     if( events[i].events & EPOLLPRI) printf( "exceptional condition on the file descriptor.\n");
     if( events[i].events & EPOLLERR) printf( "Error condition happened on the associated file descriptor\n");
     if( events[i].events & EPOLLHUP) printf( "Hang up happened on the associated file descriptor.\n");

       if( events[i].data.fd == FDChildStdout)
       {
           printf("Event in stdout of child.\n"  );
           iret= read(FDChildStdout, buff, buffLen);
/* Do not be tempted to read again, when not all data have been read at once.
 * This is a blocking fd! It is necessary to check for presence of data before another
 * attempt of read. Else it may hang forever. 
 * Same below for stderr. */           
           fprintf( stdout, "stdout from child: ");
           write( 1, buff, iret);
       } else if( events[i].data.fd == FDChildStderr)
       {
           printf("Event in stderr of child.\n"  );
           iret= read(FDChildStderr, buff, buffLen);
           fprintf( stdout, "stderr from child: ");
           write( 1, buff, iret);
       } else 
       {
           printf("Event in unknown fd!\n"  );
       }
   }
/* Wait for child to catch return code. */
    PID1= wait(&iret);
    if (PID1 < 0)
        printf("Failed to wait for process %d (errno = %d)\n", (int)PID, errno);
    else if (PID1 != PID)
        printf("Got return status of process %d (status 0x%.4X) when expecting PID %d\n",
               PID1, iret, (int)PID);
    else if (WIFEXITED(iret))
        printf("Process %d exited with normal status 0x%.4X (status %d = 0x%.2X)\n",
               PID1, iret, WEXITSTATUS(iret), WEXITSTATUS(iret));
    else if (WIFSIGNALED(iret))
        printf("Process %d exited because of a signal 0x%.4X (signal %d = 0x%.2X)\n",
               PID1, iret, WTERMSIG(iret), WTERMSIG(iret));
    else
        printf("Process %d exited with status 0x%.4X which is %s\n",
               PID1, iret, "neither a normal exit nor the result of a signal");
}
  return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.