
PHP 的 exec() 默认不保证在 bash 环境下运行,因此依赖 shell 特性(如 `通配符展开)的命令可能失败;需显式调用 bash 或改用 PHP 原生文件匹配函数(如glob()`)替代。
php 的 `exec()` 默认不保证在 bash 环境下运行,因此依赖 shell 特性(如 `*` 通配符展开)的命令可能失败;需显式调用 bash 或改用 php 原生文件匹配函数(如 `glob()`)替代。
在 PHP 中调用 exec() 执行含通配符(如 *)的命令时,常见错误是:命令在终端中能正常运行(如 mediaconvert -t wav -i /home/20220228/11/*01.rec -o test.mp3),但在 PHP 脚本中却无法匹配文件,返回空结果或报错。根本原因在于:通配符展开(globbing)是 shell 的特性,而非操作系统内核功能——而 PHP 的 exec() 默认调用的是系统默认 shell(通常是 /bin/sh),它对通配符的支持有限(POSIX sh 仅支持基础 glob,且行为受环境严格约束),尤其在 Web 服务器(如 Apache)上下文中,执行用户(如 apache 或 www-data)往往被配置为使用 /sbin/nologin 作为登录 shell,实际回退至极简 /bin/sh,不支持 bash 特有的扩展语法(如 **, {a,b}, 数组展开等)。
✅ 验证当前执行环境
首先确认 PHP 进程实际使用的 shell:
<?php exec('echo $0', $shell);
exec('echo "$SHELL"', $full_shell);
var_dump('Current shell:', $shell[0], 'SHELL env:', $full_shell[0]);
?>
- CLI 模式下常输出 /bin/bash;
- Web 模式(Apache/Nginx + PHP-FPM)下多为 /sbin/nologin 或 /bin/sh —— 此时 * 可能不展开。
? 小技巧:直接测试通配符是否生效
exec('ls -1 /home/20220228/11/*01.rec 2>/dev/null', $files); var_dump($files); // 若为空,说明 glob 未触发
✅ 解决方案一:强制使用 bash 执行(推荐用于简单场景)
通过显式调用 bash -c 包裹命令,确保通配符由 bash 解析:
$cmd = 'bash -c "mediaconvert -t wav -i /home/20220228/11/*01.rec -o /var/www/html/test.mp3"';
exec($cmd, $output, $return_code);
if ($return_code !== 0) {
error_log("Command failed with code {$return_code}: " . implode("\n", $output));
}
⚠️ 注意事项:
- 确保目标服务器已安装 bash(which bash 可验证);
- 命令字符串需用双引号包裹,内部变量/通配符才可被 bash 解析;
- 避免拼接用户输入,防止 shell 注入(应使用 escapeshellarg() 处理动态路径)。
✅ 解决方案二:用 PHP 原生 glob() 替代 shell 展开(更安全、跨平台)
当只需匹配单个或少量文件时,优先使用 PHP 内置函数,完全规避 shell 依赖:
$pattern = '/home/20220228/11/*01.rec';
$files = glob($pattern);
if (empty($files)) {
throw new RuntimeException("No files matched pattern: {$pattern}");
}
// 假设只处理第一个匹配项(符合原意“目录中仅一个文件”)
$input_file = $files[0];
$output_file = '/var/www/html/test.mp3';
$cmd = sprintf('mediaconvert -t wav -i %s -o %s',
escapeshellarg($input_file),
escapeshellarg($output_file)
);
exec($cmd, $output, $return_code);
if ($return_code !== 0) {
throw new RuntimeException("mediaconvert failed: " . implode("\n", $output));
}
✅ 优势:
- 不依赖 shell 类型,全环境一致;
- 自动处理路径中的空格、特殊字符(escapeshellarg 保障安全);
- 易于调试(可 var_dump($files) 直接查看匹配结果);
- 符合 PSR-12 编码规范,可读性与可维护性更高。
? 总结建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 快速验证、脚本简单、确定有 bash | bash -c "..." | 开发效率高,语义贴近原始命令 |
| 生产环境、Web 服务、需高安全性 | glob() + escapeshellarg() | 消除 shell 差异风险,防御注入,逻辑可控 |
| 复杂通配(如递归 **、花括号展开 {a,b}) | 必须用 bash -c | glob() 不支持此类扩展语法 |
最后提醒:无论采用哪种方式,务必检查 $return_code 并捕获 $output,避免静默失败;同时确保 Web 服务器用户对输入/输出路径具有读写权限(如 chown apache:apache /var/www/html/)。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











