如何捕获在C++中运行的命令的exit_code和stderr?

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

我正在编写一个 C++ 程序,它执行并输出(实时)shell 脚本、makefile 或只是另一个程序。但是,我希望我的程序在有错误或没有错误时以不同的方式返回。

#include "execxi.h"



using namespace std;


int execXI::run(string command)
{

    FILE *in;
    char buff[512];
    // is this the check for command execution exited with not 0?
    if(!(in = popen(command.c_str(), "r"))){
            // I want to return the exit code and error message too if any
        return 1;
    }
    // this part echoes the output of the command that's executed
    while(fgets(buff, sizeof(buff), in)!=NULL){
        cout << buff;
    }
    pclose(in);
    return 0;



}

是我到目前为止所拥有的。

假设这个脚本运行

make
来构建一个程序,它给出了这样的错误

on_target_webkit_version out/Release/obj/gen/webkit_version.h
Traceback (most recent call last):
  File "../build/webkit_version.py", line 107, in <module>
    sys.exit(main())
  File "../build/webkit_version.py", line 103, in main
    return EmitVersionHeader(*sys.argv[1:])
  File "../build/webkit_version.py", line 86, in EmitVersionHeader
    webkit_revision = GetWebKitRevision(webkit_dir, version_file)
  File "../build/webkit_version.py", line 60, in GetWebKitRevision
    version_info = lastchange.FetchVersionInfo(
AttributeError: 'module' object has no attribute 'FetchVersionInfo'
make: *** [out/Release/obj/gen/webkit_version.h] Error 1
  • 我是否可以知道这是错误退出的?

    • 是否会退出并显示代码

      else than 0
      ,因为这是一个错误?

    • 最后一部分是在

      stderr
      中输出的吗?

考虑到

make
退出时的代码不是
0
,比如说
1
,并且它在
stderr
中输出,我最终不可能捕获这些退出代码和错误消息吗?

如何捕获程序输出结果后的退出码和

stderr
,并在函数中返回
exit code
/
stderr

c++ popen stderr exit-code
4个回答
22
投票

如果您对错误代码感兴趣,这是一种比除以 256 更便携的获取方式:

printf("Exit code: %i\n", WEXITSTATUS(pclose(fp)));

但是,

popen
是一种方法,因此您要么通过 shell 中常用的重定向样式创建进一步的解决方法,要么遵循此未经测试的代码来正确执行此操作:

#include <unistd.h>
#include <stdio.h>

/* since pipes are unidirectional, we need two pipes.
   one for data to flow from parent's stdout to child's
   stdin and the other for child's stdout to flow to
   parent's stdin */

#define NUM_PIPES          2

#define PARENT_WRITE_PIPE  0
#define PARENT_READ_PIPE   1

int pipes[NUM_PIPES][2];

/* always in a pipe[], pipe[0] is for read and 
   pipe[1] is for write */
#define READ_FD  0
#define WRITE_FD 1

#define PARENT_READ_FD  ( pipes[PARENT_READ_PIPE][READ_FD]   )
#define PARENT_WRITE_FD ( pipes[PARENT_WRITE_PIPE][WRITE_FD] )

#define CHILD_READ_FD   ( pipes[PARENT_WRITE_PIPE][READ_FD]  )
#define CHILD_WRITE_FD  ( pipes[PARENT_READ_PIPE][WRITE_FD]  )

void
main()
{
    int outfd[2];
    int infd[2];

    // pipes for parent to write and read
    pipe(pipes[PARENT_READ_PIPE]);
    pipe(pipes[PARENT_WRITE_PIPE]);

    if(!fork()) {
        char *argv[]={ "/usr/bin/bc", "-q", 0};

        dup2(CHILD_READ_FD, STDIN_FILENO);
        dup2(CHILD_WRITE_FD, STDOUT_FILENO);

        /* Close fds not required by child. Also, we don't
           want the exec'ed program to know these existed */
        close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);
        close(PARENT_READ_FD);
        close(PARENT_WRITE_FD);

        execv(argv[0], argv);
    } else {
        char buffer[100];
        int count;

        /* close fds not required by parent */       
        close(CHILD_READ_FD);
        close(CHILD_WRITE_FD);

        // Write to child’s stdin
        write(PARENT_WRITE_FD, "2^32\n", 5);

        // Read from child’s stdout
        count = read(PARENT_READ_FD, buffer, sizeof(buffer)-1);
        if (count >= 0) {
            buffer[count] = 0;
            printf("%s", buffer);
        } else {
            printf("IO Error\n");
        }
    }
}

代码来自这里:

http://jineshkj.wordpress.com/2006/12/22/how-to-capture-stdin-stdout-and-stderr-of-child-program/


11
投票

子进程的返回值在前168位。你有 将 pclose 的返回值除以 256,则得到 查找子进程的返回值。

来自 http://bytes.com/topic/c/answers/131694-pclose-returning-termination-status-command

我的答案是

pclose(in)/256
是退出代码。

我仍然不知道如何以不同的方式捕获 stderr 或 sdtout,但在找到答案之前我会接受它作为我的答案。


1
投票

感谢您有关退出代码 Logan 的回复。

我相信获取 stderr 的往返是将其重定向到临时文件:

FILE* f = popen("cmd 2>/tmp/tmpfile.txt", "r");

0
投票

这个问题已经很老了,但是我仍然找不到任何真正完整且优雅的解决方案来解决这里处理的问题 - 执行操作系统命令并捕获所有 stdout、stderr 和退出状态。 所以我提出下面的例子,它不使用popen(),而是分叉一个进程并尝试拦截所有信息。还有一个标准输入管道用于更多交互式使用,但在下面的示例中未使用。 用 C 语言编码。

#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.