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

m4pnthwp  于 11个月前  发布在  其他
关注(0)|答案(4)|浏览(120)

我正在写一个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

1dkrff03

1dkrff031#

如果您对错误代码感兴趣,这是一种更便携的获取方法,而不是除以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/

64jmpszr

64jmpszr2#

子进程的返回值在前16个8位。你必须将pclose的返回值除以256,然后你得到子进程的搜索返回值。
http://bytes.com/topic/c/answers/131694-pclose-returning-termination-status-command获得
我的答案是pclose(in)/256是退出代码。
我仍然不知道如何以不同的方式捕获stderr或sdtout,但在有答案之前,我将接受这作为我的答案。

ifmq2ha2

ifmq2ha23#

谢谢你的回复关于退出代码洛根。
我相信获取stderr的往返过程是将其重定向到一个临时文件:

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

字符串

9udxz4iz

9udxz4iz4#

这个问题已经很老了,但我仍然找不到一个真正完整和优雅的解决方案来解决这里处理的问题-执行OS命令并捕获所有的stdout,stderr和退出状态。所以我提出了下面的例子,它没有使用popen(),而是分叉一个进程并试图拦截所有信息。还有一个stdin管道用于更多的交互式使用,但在下面的例子中没有使用。

#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;
}

字符串

相关问题