Friday, July 08, 2005

How to capture stdout from a spawned child process

A recent post to ace-users reminded me how hard it was for me to figure out how to capture stdout from a spawned child process in C++. After much research and trial and error, I came up with the following which uses ACE.

Update 7/12/2005: The below code does not work on Windows because Windows does not allow redirection of stdout/stderr to sockets which is what ACE_Pipe uses. See my next post for code that does work on Windows.


std::string command = ...;

ACE_Process_Options opt;

// The child process to spawn.
opt.command_line(command.c_str());

// Define the two handles for the pipe,
// fd[0] will be used for stdin
// fd[1] will be used for stdout.
ACE_HANDLE fd[2];

// Create a pipe from the two file handles.
// Pipe between stdin and stdout.
// fd[0] <-> fd[1]
ACE_Pipe pipe(fd);

// Set the stdin and stdout of the process to our file handles.
// fd[0] is used for stdin, fd[1] is used for stdout.
opt.set_handles(fd[0] /*stdin*/, fd[1] /*stdout*/);

// Spawn the child process.
ACE_Process proc;
if (proc.spawn(opt) == -1) ACE_DEBUG ((LM_ERROR, "%p\n", "spawn failed"));

// After fork:
// fd[0] <-> fd[1] child
// fd'[0] <-> fd'[1] parent

// Need to close parent stdout or parent will be trying to read from it forever.
// With stdout closed parent will get EOF from the child so that the parent
// will know the child is done.
if (ACE_OS::close(fd[1]) == -1) ACE_DEBUG ((LM_ERROR, "%p\n", "close stdout"));

// After close:
// fd[0] <-> fd[1] child
// fd'[0] <-> parent
// which leaves our (parent) stdin piped to child's stdout

// set_handles() dups the handles, so make sure they are released.
opt.release_handles();

// Read from parent's stdin, which is connected
// to the child's stdout.
std::string child_stdout;
const size_t BUFFSIZE = 1024;
char buf[BUFFSIZE + 1];
ssize_t n = 0;
while ((n = ACE_OS::read(fd[0], buf, BUFFSIZE)) > 0) {
buf[n] = 0;
child_stdout.append(buf, n);
}

ACE_exitcode exitcode = 0;
proc.wait(&exitcode);

// Close stdin.
if (ACE_OS::close(fd[0]) == -1) ACE_DEBUG ((LM_ERROR, "%p\n", "close stdin"));

// Close the pipe, pipe destructor does not close the pipe.
pipe.close();

2 comments:

Igor Gutnik said...

Thanks for very useful code

Igor Gutnik said...

Thank! This helped me alot