
PHP 的 exec() 默认不通过 bash 解释器执行命令,因此 shell 特性(如通配符展开 `)可能失效;需显式调用 bash 或改用 PHP 原生函数(如glob()`)安全匹配文件。
php 的 `exec()` 默认不通过 bash 解释器执行命令,因此 shell 特性(如通配符展开 `*`)可能失效;需显式调用 bash 或改用 php 原生函数(如 `glob()`)安全匹配文件。
在 PHP 中调用 exec() 执行含通配符(如 *)的命令时,常见错误是:命令在终端中能正常运行,但在 PHP 脚本中却无法匹配文件。例如:
// ✅ 终端中有效(bash 环境下自动展开 *)
$ ls /home/20220228/11/*01.rec
/home/20220228/11/23401.rec
// ❌ PHP 中直接 exec 失效(* 未被展开)
exec("mediaconvert -t wav -i /home/20220228/11/*01.rec -o /var/www/html/test.mp3");
根本原因在于:PHP 的 exec() 并不默认调用 bash,而是依赖系统配置的登录 shell 或 POSIX 兼容的 /bin/sh。而通配符展开(filename expansion)属于 bash/zsh 等高级 shell 的特性,标准 /bin/sh(尤其在 CentOS/RHEL 中常为 dash 或精简版 bash)可能禁用或限制 glob 行为——尤其当 Web 服务器用户(如 apache 或 www-data)的 shell 被设为 /sbin/nologin 时,实际执行环境更受限。
✅ 验证当前执行 Shell 环境
运行以下脚本确认 PHP 进程使用的 shell:
<?php exec('echo "$SHELL"', $shell_out);
exec('ps -p $$ -o comm=', $proc_out);
exec('ls -l /proc/$$/exe 2>/dev/null | awk \'{print $NF}\'', $exe_out);
var_dump([
'SHELL' => trim($shell_out[0] ?? ''),
'process' => trim($proc_out[0] ?? ''),
'exe_link' => trim($exe_out[0] ?? '')
]);
典型结果:
- CLI 执行 → SHELL: "/bin/bash"(支持 *)
- Apache + mod_php 执行 → SHELL: "/sbin/nologin",实际 shell 往往是 /bin/sh,但 /bin/sh 对通配符的支持取决于具体实现(POSIX 规范要求支持基本 glob,但某些发行版会削弱功能)。
? 小技巧验证:直接测试 exec('ls *.php', $out); —— 若返回预期文件列表,说明基础 glob 可用;若为空,则环境不支持或路径无匹配项。
✅ 推荐解决方案(按优先级排序)
方案 1:显式调用 bash(简单直接,需确保 bash 可用)
强制通过 bash -c 执行命令,启用完整 shell 功能:
$cmd = 'mediaconvert -t wav -i "/home/20220228/11/*01.rec" -o "/var/www/html/test.mp3"';
exec("/bin/bash -c " . escapeshellarg($cmd), $output, $return_code);
if ($return_code !== 0) {
error_log("Command failed with code {$return_code}: " . implode("\n", $output));
}
✅ 优势:语义清晰,兼容所有 bash 特性(*, {a,b}, $(), 管道等)
⚠️ 注意:必须使用 escapeshellarg() 严格转义整个命令字符串,防止命令注入;路径中含空格、特殊字符时尤其关键。
方案 2:用 PHP 原生 glob() 匹配文件(更安全、可控、跨平台)
避免依赖 shell,由 PHP 主动查找并逐个处理:
$pattern = '/home/20220228/11/*01.rec';
$files = glob($pattern);
if (empty($files)) {
throw new RuntimeException("No files match pattern: {$pattern}");
}
foreach ($files as $input_file) {
$output_file = '/var/www/html/' . basename($input_file, '.rec') . '.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) {
error_log("Failed to process {$input_file}: " . implode("\n", $output));
continue;
}
echo "Processed: {$input_file} → {$output_file}\n";
}
✅ 优势:零 shell 依赖、天然防注入、可精确控制流程(如跳过损坏文件、并发限制)、调试友好
✅ 适用场景:单文件匹配(如题中“仅一个文件”)、批量转换、路径动态生成
方案 3:预生成确定路径(最轻量,适合已知唯一文件)
若业务逻辑保证目录下仅有且仅有一个匹配文件,可先用 glob() 获取路径,再拼接命令:
$files = glob('/home/20220228/11/*01.rec');
$input_file = $files[0] ?? null;
if (!$input_file || !is_file($input_file)) {
throw new InvalidArgumentException("Target file not found.");
}
exec(
sprintf(
'mediaconvert -t wav -i %s -o %s',
escapeshellarg($input_file),
escapeshellarg('/var/www/html/test.mp3')
),
$output,
$return_code
);
⚠️ 关键注意事项
- 永远勿拼接用户输入:exec() 是高危函数,所有外部数据(尤其是文件路径、参数)必须经 escapeshellarg() 或白名单校验;
- 检查返回码与输出:exec() 第三参数返回 exit code,非 0 意味命令失败,应记录 $output 用于排错;
- 权限与路径:Web 服务器用户(如 www-data)需对输入/输出目录有读写权限,且 mediaconvert 命令在 $PATH 中或使用绝对路径(如 /usr/local/bin/mediaconvert);
- 超时控制:长时间运行的命令建议配合 set_time_limit(0) 和 proc_open() 实现超时与实时流捕获;
- 替代函数考量:如需获取完整输出或错误流,优先用 shell_exec()(返回字符串)或 proc_open()(全控制)。
总结
PHP 的 exec() 本身不负责 shell 特性解析,通配符 * 是否生效取决于底层执行环境。最佳实践是:优先用 glob() 在 PHP 层完成文件发现,再构造确定路径的命令;若必须依赖 shell 功能,则显式调用 /bin/bash -c 并严格转义。 这既保障了功能可靠性,又兼顾了安全性与可维护性。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











