首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 執行系統命令函數對比:system、exec、passthru、shell_exec、popen 與反引號完整解析

     編輯:PHP 綜合知識

函數對比總覽

函數輸出方式返回值獲取完整輸出獲取退出碼適用場景
system()直接輸出到標準輸出最後一行 (字符串)困難 (需輸出緩衝捕獲)第二參數 &$retval簡單命令、直接顯示結果
exec()不輸出最後一行 (字符串)第二參數 &$output 數組 (每行一元素)第三參數 &$retval**最常用**,需程序化處理輸出
passthru()直接輸出原始二進制流無 (void)困難第二參數 &$retval圖片/PDF/二進制流直接輸出瀏覽器
shell_exec()不輸出完整輸出 (字符串,含換行)直接返回無 (需自行解析)需完整輸出字符串、反引號別名
popen()管道流 (資源句柄)文件指針 (resource)fread/fgets 讀取 / fwrite 寫入pclose() 返回退出碼**雙向交互**、長時間運行進程、流式處理
反引號 `cmd`不輸出完整輸出 (字符串)直接返回無shell_exec 別名,簡潔但可讀性差

代碼示範

<?php
// 1. system
$lastLine = system('ls -la', $retval);
echo "退出碼: $retval\n";

// 2. exec (推薦)
$output = [];
$lastLine = exec('ls -la', $output, $retval);
print_r($output); // 每行一個元素
echo "最後一行: $lastLine, 退出碼: $retval\n";

// 3. passthru (輸出圖片示例)
header('Content-Type: image/png');
passthru('cat /path/to/image.png'); // 直接輸出二進制

// 4. shell_exec / 反引號
$fullOutput = shell_exec('ls -la');
// 等價
$fullOutput = `ls -la`;
echo $fullOutput;

// 5. popen (雙向交互:修改密碼示例)
$descriptors = [
    0 => ['pipe', 'r'], // stdin
    1 => ['pipe', 'w'], // stdout
    2 => ['pipe', 'w'], // stderr
];
$proc = proc_open('passwd ' . escapeshellarg($username), $descriptors, $pipes);
if (is_resource($proc)) {
    fwrite($pipes[0], $newPassword . "\n" . $newPassword . "\n");
    fclose($pipes[0]);
    $stdout = stream_get_contents($pipes[1]);
    $stderr = stream_get_contents($pipes[2]);
    fclose($pipes[1]); fclose($pipes[2]);
    $exitCode = proc_close($proc);
    // 判斷 $exitCode, $stderr
}

安全性:命令注入防範 (核心!)

**所有接收用戶輸入構建命令的場景,必須雙重轉義:**

$userInput = $_POST['filename']; // 惡意: "test.txt; rm -rf /"
// 錯誤示範
exec("cat $userInput"); // 執行了 rm -rf /

// 正確:escapeshellarg (參數級) + escapeshellcmd (命令級)
$safeArg = escapeshellarg($userInput); // 'test.txt; rm -rf /' (單引號包裹,內部單引號轉義)
exec("cat $safeArg"); // 僅嘗試讀取名為 "test.txt; rm -rf /" 的文件

// 更安全:完全避免 Shell,使用 PHP 內置函數
$content = file_get_contents($userInput); // 無命令注入風險
  • escapeshellarg($str):用單引號包裹,內部單引號轉義為 '\'',確保作為**單個參數**傳遞。
  • escapeshellcmd($str):轉義 Shell 元字符 (; & | > < ( ) $ ` \ 等),確保作為**命令本身**安全。
  • **原則**:能用 PHP 內置函數 (文件操作、網絡請求、壓縮、圖片處理) 絕不調用外部命令。

超時與後台運行處理

// 1. 設置腳本最大執行時間 (影響 system/exec/shell_exec 等待上限)
set_time_limit(300); // 5 分鐘

// 2. 命令級超時 (需外部工具)
exec("timeout 10s long_running_command", $out, $ret); // Linux timeout 命令

// 3. 後台運行 (避免阻塞 PHP 請求)
// 方法 A: 輸出重定向到 /dev/null + &
exec("nohup php /path/to/task.php > /dev/null 2>&1 &", $out, $ret);
// 方法 B: popen 寫入管道後立即關閉 (不等待)
$fp = popen("php /path/to/task.php > /dev/null 2>&1 &", 'r');
pclose($fp); // 立即返回,進程在後台運行
// 方法 C: 系統級服務 (systemd, Supervisor, Gearman, RabbitMQ Worker) —— **生產首選**

Windows 平台差異

  • exec/system 調用 cmd.exe /c,路徑分隔符、引號規則不同。
  • passthru 輸出編碼可能為 CP936/CP65001,需 iconv 轉 UTF-8。
  • popen 模式 r/w 在 Windows 僅支持單向 (PHP 7.1+ 改進)。
  • 命令路徑含空格需雙引號:exec('"C:\Program Files\Tool\tool.exe" arg')。

禁用函數配置 (php.ini)

disable_functions = exec,system,passthru,shell_exec,proc_open,proc_get_status,proc_close,proc_nice,proc_terminate,popen,pclose,pcntl_exec,pcntl_fork,pcntl_signal,pcntl_alarm,pcntl_wait,pcntl_waitpid,pcntl_wexitstatus,pcntl_wifsignaled,pcntl_wifexited,pcntl_wifstopped,pcntl_wstopsig,pcntl_wtermsig
# 視業務需要保留極少數 (如.exec 用於 Composer 安裝部署腳本),其餘嚴禁

總結與選擇建議

  1. 需獲取輸出數組、退出碼、程序化處理 → exec() (首選)。
  2. 需直接輸出二進制流 (圖片、下載) → passthru()。
  3. 需完整輸出字符串、不關心退出碼 → shell_exec() 或反引號。
  4. 需雙向交互、流式讀寫、長連接進程 → proc_open() / popen()。
  5. 簡單顯示命令結果 → system()。
  6. 生產環境原則:盡可能不用、必須用則嚴格轉義、權限最小化、審計日誌、禁用危險函數、遷移至異步隊列/內置庫/系統服務。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved