popen() / pclose() 被稱為「偽管道」,因其本質是啟動 Shell 進程執行命令,通過匿名管道連接調用進程與命令的標準輸入/輸出。雖性能不如 pipe(),但極適合「執行命令、捕獲輸出」場景。本文詳細演示 popen("env","r") 讀取環境變量 → popen("grep bash","w") 過濾的管道鏈代碼。
#includeFILE *popen(const char *command, const char *mode); // command: Shell 命令字符串(如 "ls -l", "grep foo") // mode: "r" 從命令 stdout 讀取;"w" 向命令 stdin 寫入 // 返回: 文件流指針,可用 fread/fwrite/fgets 等操作 int pclose(FILE *stream); // 關閉流,等待命令結束,返回命令退出碼
int main() {
char buffer[BUFSIZ+1] = {0};
int read_count = 0;
FILE *p_read, *p_write;
// 1. 以「讀」模式打開 env 命令:我們從 env 的 stdout 讀取
p_read = popen("env", "r");
if (!p_read) { perror("popen env"); return 1; }
// 2. 以「寫」模式打開 grep 命令:我們向 grep 的 stdin 寫入
p_write = popen("grep bash", "w");
if (!p_write) { perror("popen grep"); pclose(p_read); return 1; }
// 3. 從 env 讀取,寫入 grep,形成管道鏈
while ((read_count = fread(buffer, 1, BUFSIZ, p_read)) > 0) {
fwrite(buffer, 1, read_count, p_write);
}
// 4. 關閉(順序不影響正確性,但先關寫端讓 grep 見到 EOF)
pclose(p_write); // grep 讀到 EOF,輸出匹配行並退出
pclose(p_read); // 回收 env 進程
return 0;
}
| 調用進程操作 | mode | 命令進程對應端 | 數據流向 |
|---|---|---|---|
| 讀取命令輸出 | "r" | stdout | 命令 → 管道 → 調用進程 |
| 向命令輸入數據 | "w" | stdin | 調用進程 → 管道 → 命令 |
| 優點 | 缺點 |
|---|---|
| API 簡單,自動處理 fork/exec/管道創建 | 啟動 Shell 開銷大(fork+exec /bin/sh) |
| 支持任意 Shell 命令(管道、重定向、通配符) | 非線程安全(環境變量、信號處理衝突) |
| 返回 FILE* 兼容標準 I/O 函數 | 無法分別獲取 stdout/stderr(合併輸出) |
| 自動處理命令退出碼回收 | 命令注入風險(用戶輸入拼接 command) |
command,防止命令注入。pipe()+fork()+exec() 手工實現。pipe() 或共享內存。popen 是「調用 Shell 命令」的便捷封裝,以文件流抽象屏蔽了 fork/exec/pipe 細節。理解 "r"/"w" 模式對應調用進程視角,構建 p_read → p_write 管道鏈,即可實現命令輸出的流式處理。但請牢記:它是重量級操作,高頻調用請改用輕量級 IPC。