函數對比速查表
| 函數 | 返回值 | 特點 | 適用場景 |
exec() | 最後一行輸出 | 可選參數接收完整輸出數組、返回碼 | 需捕獲輸出、判斷退出碼 |
system() | 最後一行輸出 | 直接輸出到瀏覽器/終端,同步阻塞 | 簡單腳本、安裝腳本輸出實時顯示 |
passthru() | 無 | 直接輸出二進制數據,需配合 Header | 圖片轉換、PDF 生成等二進制流 |
shell_exec() / `反引號` | 完整輸出字符串 | 內部調用 /bin/sh -c,獲取全部輸出 | 需完整輸出文本處理 |
proc_open() | 資源句柄 | 雙向管道,可讀寫 stdin/stdout/stderr | 交互式程序、長時間運行進程控制 |
pcntl_exec() | 不返回 (成功時) | 替換當前進程映像,需 pcntl 擴展 | 守護進程啟動、權限降級 |
詳細示例
1. exec() - 最常用
<?php
$cmd = "ls -la /tmp";
$output = [];
$returnVar = 0;
exec($cmd, $output, $returnVar);
echo "Exit code: $returnVar\n";
echo "Output lines: " . count($output) . "\n";
foreach ($output as $line) {
echo "$line\n";
}
?>
2. shell_exec() - 獲取完整輸出
<?php
// 反引號語法糖
$output = `df -h 2>&1`; // 合併 stderr
echo "$output
";
?>
3. proc_open() - 完全控制
<?php
$descriptors = [
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'] // stderr
];
$proc = proc_open('php -r "echo \"stdout\\n\"; fwrite(STDERR, \"stderr\\n\");"', $descriptors, $pipes);
if (is_resource($proc)) {
// 寫入 stdin (如需要)
// fwrite($pipes[0], "input data");
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$return = proc_close($proc);
echo "STDOUT: $stdout\nSTDERR: $stderr\nExit: $return\n";
}
?>
4. passthru() - 二進制輸出
<?php
header('Content-Type: image/png');
// ImageMagick 轉換並直接輸出圖片
passthru('convert logo: -resize 200x200 png:-');
?>
安全紅線 (必讀)
- 絕對禁止 直接拼接用戶輸入:
exec("ls $userInput") → 命令注入
- 必須使用
escapeshellarg() (單個參數) 或 escapeshellcmd() (整條命令)
- 原則:白名單驗證命令參數,避免 Shell 解析,優先用
proc_open 繞過 Shell
<?php
// 安全示例
$filename = $_GET['file'] ?? '';
$safeName = escapeshellarg(basename($filename)); // 僅允許文件名,禁止路徑遍歷
exec("cat $safeName", $out, $ret);
?>
性能與選型建議
- 高頻調用:避免啟動 Shell,改用 PHP 內置函數 (
glob, scandir, file_get_contents 等)
- 長時間任務:
proc_open + 異步讀取管道,或寫入任務隊列由 Worker 消費
- 權限最小化:Web 用戶 (www-data) 勿賦予 sudo 權限,敏感操作走獨立守護進程 + IPC