
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 解析:
立即学习 “PHP 免费学习笔记(深入)”;
$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/)。