PHP中如何将exec调用的输出(含stderr)重定向至调用程序?及子进程输出未写入日志的问题排查
解决PHP中exec子进程输出(含stderr)重定向到父进程的问题
你的问题核心在于exec函数默认不会将子进程的输出直接传递到父进程的输出流,哪怕你加了2>&1把stderr合并到stdout,这些输出也会被exec直接丢弃,不会自动同步到父进程的输出管道,所以父进程的重定向(> t.log)只能捕获到自身printf的内容。
为什么你的当前代码不生效?
看你这段代码:
exec('/usr/bin/php -f t.php 2 2>&1');
exec函数如果不指定第二个参数(用于捕获输出的数组),它会直接丢弃子进程的stdout内容——哪怕你用2>&1把stderr合并进来,这些内容也不会自动输出到父进程的控制台或重定向目标中,自然不会出现在t.log里。
解决方案1:用proc_open实现输出继承(推荐)
proc_open比exec更灵活,可以让子进程直接继承父进程的stdout和stderr,这样父进程的重定向会自动包含子进程的所有输出,完全不需要硬编码日志文件名。
修改后的代码:
<?php printf("$argv[1]: ". date("H:i:s")."\n"); if ($argv[1] == '1') { // 定义子进程的文件描述符,直接复用父进程的标准流 $descriptorspec = [ 0 => STDIN, // 子进程使用父进程的输入 1 => STDOUT, // 子进程输出直接写入父进程的标准输出 2 => STDERR // 子进程错误输出直接写入父进程的标准错误 ]; // 启动子进程 $process = proc_open('/usr/bin/php -f t.php 2', $descriptorspec, $pipes); // 等待子进程结束并释放资源 proc_close($process); } ?>
执行php -f t.php 1 > t.log后,父进程和子进程的输出都会被写入t.log,完全符合你的预期。
解决方案2:用exec捕获输出后手动打印
如果你坚持使用exec,可以通过捕获输出数组,再手动将内容打印到父进程的输出流,这样重定向就能包含子进程的内容:
<?php printf("$argv[1]: ". date("H:i:s")."\n"); if ($argv[1] == '1') { // 捕获子进程的所有输出(含stderr,因为2>&1) exec('/usr/bin/php -f t.php 2 2>&1', $outputLines, $returnCode); // 逐行打印捕获到的输出,让父进程的重定向能捕获到 foreach ($outputLines as $line) { echo $line . "\n"; } } ?>
这个方案同样不需要硬编码日志文件名,完全依赖父进程的命令行重定向完成日志写入。
验证结果
无论使用哪种方案,执行php -f t.php 1 > t.log后,t.log都会包含:
1: 15:10:52 2: 15:10:53
内容的提问来源于stack exchange,提问作者Walter Schrabmair
相关产品推荐
相关产品推荐

