exec 的 $output 参数必须传引用,否则数组为空;其返回值仅是最后一行字符串,完整输出需用 $output 或 shell_exec();捕获 stderr 必须加 2>&1;Web 环境需注意超时、权限、stdin 阻塞及 disable_functions 限制。

exec 的 $output 参数必须传引用,否则数组收不到任何内容
很多人写 exec('ls', $output) 后发现 $output 还是空数组,根本原因是没加引用符号 &。PHP 的 exec 要求第二个参数是「引用传入的数组」,否则内部无法往里写数据。
正确写法必须是:exec('ls -l', $output, $return_var) 前提是 $output 已声明为数组,且函数调用时 PHP 自动按引用处理(PHP 7+ 中,array &$output 是函数签名强制要求,但调用时不显式写 & —— 重点在于变量本身得是可写引用目标,不能是表达式或临时值)。
- 错误示范:
$output = exec('date', []);→[]是临时数组,无法被填充 - 正确示范:
$output = []; exec('date', $output, $return_var); - 如果复用同一个
$output变量多次调用exec,记得每次前用unset($output)或$output = []清空,否则结果会追加而非覆盖
为什么 exec 默认只返回最后一行?别依赖它的返回值取完整输出
exec() 函数的返回值类型是 string|false,它**只返回命令输出的最后一行(不含换行符)**,不是全部内容。这个设计容易误导人以为它像 shell_exec() 一样返回完整字符串。
- 例如
exec('printf "a\nb\nc"')返回的是"c",$output才是["a", "b", "c"] - 如果你只写
$last = exec('ps aux');,就丢掉了前面所有行,还可能因命令无输出而得到false - 真正需要完整文本时,优先选
shell_exec();需要逐行处理或只关心几行关键日志时,才用exec配合$output
stderr 不合并就看不到错误,2>&1 不是可选项而是必选项
很多服务类命令(比如 curl、php -S、自定义脚本)出错时把提示打到 stderr,而 exec 默认只捕获 stdout。不显式重定向,$output 数组里就什么都没有,$return_var 却是非 0,造成“执行失败但没日志”的假象。
立即学习“PHP免费学习笔记(深入)”;
- 务必在命令末尾加上
2>&1:例如exec('curl -s http://localhost:8000/health 2>&1', $output, $return_var) - 如果命令本身含管道或重定向,注意 shell 解析顺序,建议整个命令用单引号包裹,变量用
escapeshellarg()拼接 - 某些后台服务启动后立即返回(stdout 空,但 stderr 有 warning),不加
2>&1就完全无法诊断
Web 环境下 exec 卡住?检查超时、权限和后台进程行为
在 Apache/Nginx + PHP-FPM 场景中,exec 表现异常往往不是语法问题,而是运行时约束:
- 命令未设超时:用
timeout 3s your-command 2>&1防止挂死(Linux);Windows 可用start /b cmd /c "your-command & exit"配合sleep曲线救国 - Web 用户(如
www-data)无权访问目标端口、二进制或配置文件:用exec('whoami 2>&1', $out)和exec('ls -l /path 2>&1', $out)快速定位权限断点 - 命令试图读取 stdin(如交互式脚本)或守护进程化(fork 后父进程退出):这类命令必须重定向 stdin(
</dev/null)并确保不脱离终端,否则 PHP 进程会阻塞等待 -
disable_functions里禁用了exec:用ini_get('disable_functions')或phpinfo()确认,别猜
最易被忽略的一点:即使 $output 有内容、$return_var === 0,也不能代表业务逻辑成功——比如 curl 返回 HTTP 500 时状态码仍是 0,得自己解析响应体或加 -f 参数让 curl 在非 2xx 时退出非 0。



















