
`shell_exec()` 默认只捕获标准输出(stdout),而像 `pdftotext -v` 这类工具将版本信息输出到标准错误(stderr),导致返回 `null`;需显式重定向 stderr 到 stdout(即添加 `2>&1`)才能获取完整输出。
在 Laravel 或任何 PHP 环境中调用命令行工具(如 pdftotext、pdftohtml、convert 等)进行诊断或集成时,一个常见却易被忽略的问题是:命令在终端中正常执行并显示结果,但在 PHP 中调用却返回空值或无输出。例如:
dump(shell_exec('pdftotext -v')); // null而终端中直接运行完全正常:
$ pdftotext -v pdftotext version 21.12.0
根本原因在于:pdftotext -v(以及许多 GNU/Linux/macOS 工具的 -v、--version 选项)默认将版本信息写入 stderr 而非 stdout。而 PHP 的 shell_exec()、exec() 等函数默认仅捕获 stdout,stderr 被静默丢弃——除非你显式重定向。
✅ 正确做法:使用 2>&1 将标准错误重定向至标准输出:
立即学习“PHP免费学习笔记(深入)”;
$output = shell_exec('pdftotext -v 2>&1');
dump($output); // "pdftotext version 21.12.0\n"
// 更健壮的写法(带错误码检查)
exec('pdftotext -v 2>&1', $outputLines, $returnCode);
$fullOutput = implode("\n", $outputLines);
dump($fullOutput, $returnCode); // 输出内容 + 退出码(0 表示成功)⚠️ 注意事项:
2>&1 必须写在命令末尾,且不能有空格(如 2> &1 无效);
-
在 Web 环境(如 Laravel API 或 Vapor)中,PHP 运行用户(如 www-data、nginx 或 Vapor 的 ec2-user)可能没有权限访问某些二进制路径,建议使用绝对路径(可通过 which pdftotext 获取):
$path = '/usr/local/bin/pdftotext'; // 或 '/opt/homebrew/bin/pdftotext'(macOS Homebrew) $output = shell_exec("{$path} -v 2>&1"); -
在 Laravel 中,推荐封装为安全的诊断方法,并增加超时与白名单校验,避免命令注入:
public function diagnostic() { $tools = ['pdftotext', 'pdftohtml', 'gs', 'convert']; $results = []; foreach ($tools as $tool) { $cmd = escapeshellcmd("which {$tool} 2>&1"); $binary = trim(shell_exec($cmd)); if (!$binary) { $results[$tool] = ['error' => 'not found']; continue; } $versionCmd = escapeshellcmd("{$binary} -v 2>&1"); $version = trim(shell_exec($versionCmd)); $results[$tool] = [ 'binary' => $binary, 'version' => $version ?: 'no version output', ]; } return response()->json($results); }
? 总结:PHP 的进程执行函数不自动合并 stderr,这是 Unix/Linux 进程模型的设计使然。要可靠获取 CLI 工具的全部输出(尤其是诊断类命令),请始终添加 2>&1 —— 它不是“修复”,而是对标准 I/O 行为的正确适配。











