pclose() 函数用于关闭由popen()打开的进程文件指针。
当通过popen()打开一个进程后,可以通过fread()、fgets()等函数读取进程输出,最后必须使用pclose()关闭进程文件指针,以释放资源和获取进程的退出状态。
pclose ( resource $handle ) : int
| 参数 | 类型 | 说明 |
|---|---|---|
handle |
资源 | 由popen()函数返回的文件指针资源 |
返回进程的退出状态代码。如果发生错误,返回-1。
在Unix/Linux系统中,退出状态0通常表示成功,非零值表示错误。
popen()打开一个进程管道pclose()关闭管道,获取进程退出状态<?php
// 使用popen()打开进程,执行ls命令
$handle = popen('ls -la', 'r');
if ($handle === false) {
die("无法打开进程");
}
echo "当前目录内容:\n";
echo "--------------\n";
// 读取命令输出
while (!feof($handle)) {
$output = fread($handle, 4096);
echo $output;
}
// 关闭进程并获取退出状态
$returnValue = pclose($handle);
echo "\n--------------\n";
echo "命令执行完成,退出状态: " . $returnValue . "\n";
// 根据退出状态判断结果
if ($returnValue === 0) {
echo "命令执行成功\n";
} else {
echo "命令执行失败\n";
}
?>
<?php
// 打开grep进程,用于过滤文本
$handle = popen('grep -i "error"', 'w');
if ($handle === false) {
die("无法打开进程");
}
// 要过滤的日志数据
$logData = [
"INFO: Application started",
"ERROR: Database connection failed",
"WARNING: Memory usage high",
"ERROR: File not found",
"INFO: Request processed"
];
// 向grep进程写入数据
foreach ($logData as $line) {
fwrite($handle, $line . "\n");
}
// 关闭进程
$status = pclose($handle);
echo "grep进程执行完成,状态码: " . $status . "\n";
echo "\n注意:grep命令在找到匹配行时返回0,否则返回1\n";
?>
<?php
function executeCommand($command, $mode = 'r') {
$handle = @popen($command, $mode);
if ($handle === false) {
throw new Exception("无法执行命令: $command");
}
return $handle;
}
function closeProcess($handle) {
if (is_resource($handle)) {
$status = pclose($handle);
return $status;
}
return -1;
}
try {
// 执行命令
$command = 'find /var/log -name "*.log" 2>/dev/null | head -10';
$handle = executeCommand($command, 'r');
// 读取输出
$output = '';
while (!feof($handle)) {
$output .= fread($handle, 8192);
}
// 关闭进程
$status = closeProcess($handle);
echo "命令: $command\n";
echo "输出:\n$output\n";
echo "退出状态: $status\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
?>
<?php
echo "方法1: 使用popen()/pclose()\n";
echo "-------------------------\n";
$popenHandle = popen('echo "Hello from popen" && sleep 1', 'r');
$popenOutput = fread($popenHandle, 1024);
$popenStatus = pclose($popenHandle);
echo "输出: " . trim($popenOutput) . "\n";
echo "状态: $popenStatus\n";
echo "\n方法2: 使用proc_open()/proc_close()\n";
echo "---------------------------------\n";
$descriptors = [
0 => ["pipe", "r"], // 标准输入
1 => ["pipe", "w"], // 标准输出
2 => ["pipe", "w"] // 标准错误
];
$procHandle = proc_open('echo "Hello from proc_open" && sleep 1', $descriptors, $pipes);
if (is_resource($procHandle)) {
fclose($pipes[0]); // 关闭输入
$procOutput = stream_get_contents($pipes[1]); // 读取输出
fclose($pipes[1]);
fclose($pipes[2]);
$procStatus = proc_close($procHandle);
echo "输出: " . trim($procOutput) . "\n";
echo "状态: $procStatus\n";
}
echo "\n对比说明:\n";
echo "- popen() 更简单,只能单向通信(读或写)\n";
echo "- proc_open() 更灵活,可以双向通信,但代码更复杂\n";
?>
| 返回值 | 说明 |
|---|---|
| 0 | 进程成功执行并正常退出(大多数Unix/Linux命令) |
| 1-255 | 进程执行出错,具体含义由执行的命令决定 |
| -1 | pclose()函数本身出错(如无效的文件指针) |
| 问题 | 解决方案 |
|---|---|
| pclose()返回-1 | 检查文件指针是否有效,确保之前调用了popen() |
| 进程成为僵尸进程 | 确保每个popen()都有对应的pclose()调用 |
| 命令执行超时 | 考虑使用proc_open()配合流超时设置,或使用后台执行 |
| 权限不足 | 检查运行PHP的用户是否有执行命令的权限 |
| 命令输出被缓冲 | 某些命令会缓冲输出,尝试使用无缓冲模式或强制刷新 |
| 函数 | 双向通信 | 复杂性 | 适用场景 |
|---|---|---|---|
| popen()/pclose() | 单向 | 简单 | 简单的命令执行,只需读取输出或只提供输入 |
| proc_open()/proc_close() | 双向 | 复杂 | 需要与进程交互的复杂场景 |
| exec() | 无 | 简单 | 只需执行命令,不关心输出 |
| shell_exec() | 无 | 简单 | 获取命令的全部输出 |