ZetCode

PHP popen 函数

最后修改于 2025 年 4 月 3 日

PHP 的 popen 函数打开一个进程文件指针。它允许与外部命令进行双向通信。这对于执行系统命令和处理它们的输入/输出非常有用。

基本定义

popen 函数通过执行一个命令来打开一个到进程的管道。它返回一个类似于 fopen 返回的文件指针。

语法:popen(string $command, string $mode): resource|false。模式可以是 "r" 表示读取,"w" 表示写入。始终使用 pclose 关闭。

基本 popen 示例

这展示了 popen 读取命令输出的最简单用法。

basic_popen.php
<?php

declare(strict_types=1);

$handle = popen('ls -l', 'r');
if ($handle === false) {
    exit("Failed to open process");
}

while (!feof($handle)) {
    echo fgets($handle);
}

pclose($handle);

这会执行 'ls -l' 命令并逐行读取其输出。完成后使用 pclose 关闭文件指针。始终检查失败情况。

写入进程

我们也可以使用 "w" 模式将数据写入命令的输入。

popen_write.php
<?php

declare(strict_types=1);

$handle = popen('grep "error" > errors.log', 'w');
if ($handle === false) {
    exit("Failed to open process");
}

fwrite($handle, "error: file not found\n");
fwrite($handle, "warning: deprecated function\n");
fwrite($handle, "error: permission denied\n");

pclose($handle);

这会将行写入 grep 命令,该命令过滤 "error" 消息。过滤后的输出被重定向到 errors.log。进程通过管道接收输入。

双向通信

为了实现双向通信,我们需要两次调用 popen

two_way.php
<?php

declare(strict_types=1);

$writeHandle = popen('bc', 'w');
$readHandle = popen('bc', 'r');

if ($writeHandle === false || $readHandle === false) {
    exit("Failed to open process");
}

fwrite($writeHandle, "5 + 7\n");
echo "Result: " . fgets($readHandle);

fwrite($writeHandle, "10 * 3\n");
echo "Result: " . fgets($readHandle);

pclose($writeHandle);
pclose($readHandle);

这演示了与 bc 计算器的通信。请注意,这种方法存在局限性——适当的进程控制需要使用 proc_open 替代。

处理大型输出

popen 对于处理大型命令输出非常高效。

large_output.php
<?php

declare(strict_types=1);

$handle = popen('find /var/log -type f -name "*.log"', 'r');
if ($handle === false) {
    exit("Failed to open process");
}

$count = 0;
while (!feof($handle)) {
    $file = trim(fgets($handle));
    if (!empty($file)) {
        $count++;
        echo "Found log file: {$file}\n";
    }
}

pclose($handle);
echo "Total log files found: {$count}\n";

这会在 /var/log 中查找所有 .log 文件,而无需将所有结果加载到内存中。每行在读取时都被处理,使其对于大型输出具有内存效率。

错误处理

与进程协同工作时,妥善处理错误至关重要。

error_handling.php
<?php

declare(strict_types=1);

$command = 'nonexistent-command 2>&1';
$handle = popen($command, 'r');

if ($handle === false) {
    exit("Failed to open process");
}

$output = '';
while (!feof($handle)) {
    $output .= fgets($handle);
}

$status = pclose($handle);
if ($status !== 0) {
    echo "Command failed with status: {$status}\n";
    echo "Error output: {$output}\n";
} else {
    echo "Command succeeded. Output:\n{$output}";
}

这展示了如何捕获输出和错误流 (2>&1)。我们使用 pclose 检查进程的退出状态。始终处理潜在的错误。

最佳实践

来源

PHP popen 文档

本教程介绍了 PHP 的 popen 函数,并通过实际示例展示了其在不同场景下用于进程通信的用法。

作者

我叫 Jan Bodnar,我是一名热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出 所有 PHP 文件系统函数