PHP CLI 实时逐行输出需禁用两级缓冲:先调用 ob_end_flush() 或 ob_flush() 清 PHP 缓冲(若启用),再用 flush() 送入系统缓冲,最后关键一步是 fflush(STDOUT) 强制刷新终端;Windows 下建议加 stream_set_write_buffer(STDOUT, 0) 彻底禁用写缓冲。

PHP CLI 脚本如何实现逐行实时输出
默认情况下,PHP CLI 的 echo 或 print 输出会被系统缓冲,直到脚本结束或缓冲区满才刷到终端。要看到“实时”效果(比如跑任务时逐行打印日志),必须主动控制输出缓冲行为。
关键不是加什么魔法函数,而是关掉 PHP 和系统的两级缓冲:
- 调用
ob_end_flush()或ob_flush()清空 PHP 输出缓冲(如果开启) - 用
flush()强制将 PHP 缓冲内容送入系统缓冲 - 用
fflush(STDOUT)强制操作系统把 stdout 缓冲立即写到终端(这才是 CLI 下真正起效的一步) - 避免使用
echo "xxx\n"后直接跟sleep()—— 某些终端或 SSH 客户端会自己做行缓冲,可改用echo "xxx" . PHP_EOL并确保换行符存在
为什么 ob_flush() + flush() 在 CLI 下经常失效
因为 CLI SAPI 默认不启用输出缓冲(output_buffering = 0),所以 ob_flush() 往往无事可做;而 flush() 对 CLI 的 stdout 实际上没有控制权 —— 它只对 Web SAPI(如 Apache、FPM)下的响应流有效。
真正起作用的是:fflush(STDOUT)。它直接操作 C 标准库的 stdout 流,强制刷新底层文件描述符缓冲。
立即学习“PHP免费学习笔记(深入)”;
示例片段:
Windows CMD / PowerShell 下的特殊处理
Windows 终端对行缓冲更敏感,即使加了 fflush(STDOUT),有时仍卡住不显示,原因通常是:
- CMD 默认启用“快速编辑模式”,会暂停输出(尤其在鼠标选中时)
- PowerShell 6+ 默认使用不同 I/O 策略,部分版本需额外调用
stream_set_blocking(STDOUT, true) - 确保每行以
\n结尾(Windows 不要求\r\n,反而是混用可能触发异常缓冲)
稳妥做法:开头加一句 stream_set_write_buffer(STDOUT, 0),彻底禁用 C 库层的写缓冲(PHP 7.4+ 支持)。
配合进度条或覆盖式输出时的陷阱
想用 \r 回车覆盖当前行(比如下载进度),要注意:
-
\r不是换行,不会触发终端自动刷新,必须配fflush(STDOUT) - 某些 IDE 内置终端(如 VS Code 的 integrated terminal)不完全支持
\r覆盖,会堆叠打印,建议优先用真实终端验证 - 别用
echo "\rDone!";就完事 —— 前面如果输出过不含\n的内容,且没 flush,\r会作用在错误位置
最小可靠进度模拟:
真正卡住的地方往往不在 PHP 层,而在你没意识到的终端行为或系统 stdio 缓冲策略上。每次怀疑输出不实时,先试
fflush(STDOUT),再查终端环境,最后看是不是脚本被信号中断或超时杀掉了。











