views:

2757

answers:

3

How do I launch an app and capture the output via stdout and maybe stderr?

I am writing an automated build system and I need to capture the output to analyze. I'd like to update the svn repo and grab the revision number so I can move the files in autobuild/revNumber/ if successful. I also would like to build using make and upload the compile text to my server for everyone to see the warnings and errors on a failed build.

I can't find the system() function, but I found the CreateProcess() function on MSDN. I am able to launch what I need but I have no idea how to capture the stderr and stdout. I notice the process launches separately unless I set a breakpoint and keep my app exiting which it then will keep all the text in my app console window. I would also want to wait until all processes are finished and then scan the data it produced to do any additional operations I need. How do I do any of this?

+8  A: 

In real shells (meaning, not sea shells - I mean, not in C Shell or its derivatives), then:

program arg1 arg2 >/tmp/log.file 2>&1

This runs program with the given arguments, and redirects the stdout to /tmp/log.file; the notation (hieroglyph) '2>&1' at the end sends stderr (file descriptor 2) to the same place that stdout (file descriptor 1) is going. Note that the sequence of operations is important; if you reverse them, then standard error will go to where standard output was going, and then standard output (but not standard error) will be redirected to the file.

The choice of file name shown is abysmal for numerous reasons - you should allow the user to choose the directory, and probably should include the process ID or time stamp in the file name.

LOG=${TMPDIR:-/tmp}/log.$$.$(date +%Y%m%d-%H%M%S)
program arg1 arg2 >$LOG 2>&1

In C++, you can use the system() function (inherited from C) to run processes. If you need to know the file name in the C++ program (plausible), then generate the name in the program (strftime() is your friend) and create the command string with that file name. (Strictly, you also need getenv() to get $TMPDIR, and the POSIX function getpid() to get the process ID, and then you can simulate the two-line shell script (though the PID used would be of the C++ program, not the launched shell).

You could instead use the POSIX popen() function; you'd have to include the '2>&1' notation in the command string that you create to send the standard error of the command to the same place as standard output goes, but you would not need a temporary file:

FILE *pp = popen("program arg1 arg2 2>&1", "r");

You can then read off the file stream. I'm not sure whether there's a clean way to map a C file stream into a C++ istream; there probably is.

Jonathan Leffler
sheepsimulator
Jonathan Leffler
sheepsimulator
+4  A: 

You need to fill up the STARTUP_INFO structure, which has hStdInput, hStdOutput and hStdError. Remember to inherit handles when you CreateProcess.

/* Assume you open a file handle or pipe called myoutput */
STARTUP_INFO si_startinfo;
ZeroMemory(&si_startinfo, sizeof(STARTUP_INFO));
si_startinfo.cb = sizeof(STARTUP_INFO);
si_startinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si_startinfo.hStdOutput = myoutput;
si_startinfo.hStdError = myoutput;
si_startifno.dwFlags != STARTF_USEHANDLES;

PROCESS_INFORMATION pi_procinfo;
ZeroMemory(&pi_procinfo, sizeof(PROCESS_INFORMATION);

CreateProcess(NULL, cmdline, NULL, NULL, true, 0, NULL, pathname, &si_startinfo, &pi_procinfo);

I have not shown the error handling aspects, which you will need to do. The 5th argument is set to true to inherit the handles. Others have explained how to create pipes so I won't repeat it here.

sep
A: 

Microsoft's CRTs and the MSDN library do include the system function and the _popen function.

Windows programmer