8

如何启动应用程序并通过 stdout 和 stderr 捕获输出?

我正在编写一个自动构建系统,我需要捕获输出以进行分析。我想更新 svn repo 并获取修订号,以便如果成功,我可以将文件移动到 autobuild/revNumber/ 中。我还想使用 make 构建并将编译文本上传到我的服务器,让每个人都能看到构建失败的警告和错误。

我找不到该system()功能,但我CreateProcess()在MSDN上找到了该功能。我能够启动我需要的东西,但我不知道如何捕获标准错误和标准输出。我注意到该进程单独启动,除非我设置断点并让我的应用程序退出,然后它将所有文本保留在我的应用程序控制台窗口中。我还想等到所有进程完成,然后扫描它产生的数据以执行我需要的任何其他操作。我该怎么做?

4

3 回答 3

11

在真正的贝壳中(意思是,不是贝壳——我的意思是,不是在 C Shell 或其衍生物中),然后:

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

这将使用给定的参数运行程序,并将标准输出重定向到 /tmp/log.file;符号(象形文字) ' 2>&1' 最后将 stderr (文件描述符 2) 发送到 stdout (文件描述符 1) 所在的位置。请注意,操作顺序很重要;如果您反转它们,那么标准错误将转到标准输出所在的位置,然后标准输出(但不是标准错误)将被重定向到文件。

显示的文件名选择很糟糕,原因有很多——您应该允许用户选择目录,并且可能应该在文件名中包含进程 ID 或时间戳。

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

在 C++ 中,您可以使用system()函数(从 C 继承)来运行进程。如果您需要知道 C++ 程序中的文件名(似是而非),则在程序中生成名称(strftime()是您的朋友)并使用该文件名创建命令字符串。(严格来说,您还需要getenv()获取 $TMPDIR,以及getpid()获取进程 ID 的 POSIX 函数,然后您可以模拟两行 shell 脚本(尽管使用的 PID 是 C++ 程序的,而不是启动的 shell)。

您可以改为使用 POSIXpopen()函数;您必须2>&1在创建的命令字符串中包含“”符号,以将命令的标准错误发送到与标准输出相同的位置,但您不需要临时文件:

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

然后,您可以读取文件流。我不确定是否有一种干净的方法可以将 C 文件流映射到 C++ istream;可能有。

于 2008-11-19T00:42:34.150 回答
4

您需要填写 STARTUP_INFO 结构,该结构具有 hStdInput、hStdOutput 和 hStdError。请记住在 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);

我没有展示你需要做的错误处理方面。第 5 个参数设置为 true 以继承句柄。其他人已经解释了如何创建管道,所以我不会在这里重复。

于 2008-11-19T00:56:59.673 回答
0

Microsoft 的 CRT 和 MSDN 库确实包含 system 函数和 _popen 函数。

于 2008-11-19T01:00:21.417 回答