c popen 不会捕获 stderr

发布于 2024-11-27 20:14:29 字数 167 浏览 1 评论 0原文

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

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

popen("nedit", "r");

但是我在屏幕上看到了所有关于非 utf8 的垃圾......

I'm trying to use popen() to catch the stderr of a call, but of course it doesn't seem to be doing that. Any ideas?

My code looks more or less like this:

popen("nedit", "r");

But I'm getting all this garbage about non-utf8 on my screen...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

哭泣的笑容 2024-12-04 20:14:29

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

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

或者,如果您根本不需要 stdout,

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

(除 /dev/null 之外的任何其他文件都可以接受出色地。)

popen gives you a file handle on a process' stdout, not its stderr. Its first argument is interpreted as a shell command, so you can do redirections in it:

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

or, if you don't want the stdout at all,

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

(Any other file besides /dev/null is acceptable as well.)

不必你懂 2024-12-04 20:14:29

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

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

If you want to discard all of the error messages, then you can use:

popen("nedit 2>/dev/null", "r");
差↓一点笑了 2024-12-04 20:14:29

如果您同时需要 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 生成大量输出/错误,则必须更仔细地处理流(见下面的评论)。

if you need both stdout and stderr streams, you can open an extra pipe and redirect child stderr to it:

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]);

As Andrew pointed out, if command generates lots of output/errors, you must handle the streams more carefully (see comments below).

红尘作伴 2024-12-04 20:14:29

安德烈·路易斯·达席尔瓦·蒙泰罗的解决方案确实非常出色。
只是为了提供额外的选择,我添加了下面的提案。取代了 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;
}

The solution of Andre Luis da Silva Monteiro is really brilliant.
Just for additional choice, I am adding the proposal below. Instead of popen(), a new process is forked, supplying all connecting pipes - stdin, stdout and stderr.
The comment of Andrew Henle applies as well.


#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文