exec函数需传入第三个参数$return_var才能获取脚本退出码,其值为整数:0表示成功,非0表示失败(如127为命令未找到);忽略该参数则无法获取退出码。

exec 函数怎么拿到脚本的退出码
PHP 的 exec 默认不返回退出码,只返回最后一行输出。要拿到退出码,必须显式传入第三个参数 $return_var —— 这个变量会被赋值为子进程的终止状态(0 表示成功,非 0 表示异常)。
常见错误是只写前两个参数,比如 exec('ls /tmp') 或 exec('ls /tmp', $output),这时 $return_var 完全没被使用,退出码就丢了。
实操建议:
- 始终把
$return_var作为第三个参数传入,哪怕只是临时变量,例如:exec('sh /path/to/script.sh', $output, $return_var) - 调用后立刻检查
$return_var类型和值:它总是整数,但某些 shell 错误(如命令不存在)可能返回 127,权限拒绝可能是 126,别默认“非 0 就是脚本逻辑错” - 如果脚本本身用
exit 3显式退出,$return_var就是 3;但如果脚本被信号终止(如kill -9),高位会置位,实际值可能是 137(128 + 9),PHP 不自动解码,得自己判断
为什么 exec 执行完 $return_var 总是 0
最常见原因是命令根本没执行成功,但 PHP 没报错,$return_var 却显示 0 —— 这通常是因为 shell 解析失败,而底层 execve() 调用返回了 ENOENT,但 PHP 的 exec 在找不到解释器时仍可能返回 0(尤其在某些 CGI/SAPI 模式下)。
立即学习“PHP免费学习笔记(深入)”;
实操建议:
- 先确认命令路径绝对正确:
exec('/bin/sh /full/path/to/script.sh', $output, $return_var),避免依赖$PATH - 加
2>&1把 stderr 合并进输出,看是否报 “command not found” 或 “Permission denied”:exec('/bin/sh /script.sh 2>&1', $output, $return_var) - 用
which sh或readlink -f /bin/sh确认系统真实 shell 路径,有些容器环境里/bin/sh是软链到dash,不支持某些 bash 特性
exec 在 PHP7.1 下的兼容性注意点
PHP7.1 对 exec 本身没有行为变更,但默认启用的 open_basedir 和 disable_functions 可能直接禁用它,且错误提示极不明确 —— exec 返回空、$output 为空数组、$return_var 为 0,看起来像成功,实则是被拦截了。
实操建议:
- 检查
phpinfo()或运行var_dump(ini_get('disable_functions')),确认exec没在禁用列表里 - 检查
open_basedir是否限制了脚本所在目录或/tmp(很多临时文件写在这里) - PHP7.1 已废弃
safe_mode,但如果你从老版本迁移过来,记得清理相关配置,否则可能残留干扰 - 如果用的是 suhosin(虽已淘汰),它默认会拦截带管道或重定向的命令,
exec('cmd | grep x')会静默失败
获取完整输出和退出码的最小安全写法
不要依赖 exec 的返回值(它是最后一行),也不要忽略 $output 数组和 $return_var 的配合。一个健壮的调用必须同时处理三者。
示例:
exec('/usr/bin/python3 /opt/scripts/check.py arg1 2>&1', $output, $return_var);
if ($return_var !== 0) {
error_log('Script failed with code ' . $return_var . ': ' . implode("\n", $output));
// 处理失败逻辑
}
$full_output = implode("\n", $output);
关键点:
-
2>&1必须加,否则错误信息丢失,$output里只有 stdout -
$output是数组,每行一个元素,implode才是完整字符串 - 别用
shell_exec替代 —— 它不提供退出码,还得额外调用proc_get_status,更重且难控 - 如果脚本耗时长,记得设
set_time_limit(0),否则 PHP 脚本超时后子进程可能变成僵尸
退出码不是万能的,尤其当脚本内部用 try/catch 吞掉异常却忘了 exit(1),这时候 $return_var 还是 0。真要可靠,脚本自身得规范退出。



















