如何在 PHP 中正确捕获 shell 命令的标准错误输出(stderr)

如何在 PHP 中正确捕获 shell 命令的标准错误输出(stderr)

`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 表示成功)

⚠️ 注意事项:

Magic Write

Magic Write

Canva旗下AI文案生成器

下载

  • 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 行为的正确适配。

https://www.php.cn/faq/1997969.html

发表回复

Your email address will not be published. Required fields are marked *