管道 (Unix)

文字終端機上一個包含三個程式的管道

在類 Unix 作業系統（以及一些其他借用了這個設計的作業系統，如 Windows）中，管道（Pipeline）是一系列將標準輸入輸出連結起來的程序，其中每一個程序的輸出被直接作為下一個程序的輸入。 每一個連結都由匿名管道實現。管道中的組成元素也被稱作。

這個概念是由道格拉斯・麥克羅伊為 Unix 命令列發明的，因與物理上的管道相似而得名。

例子

1.1 簡單樣例

ls -l | less

在這個例子中，ls 用於在 Unix 下列出目錄內容，less 是一個有搜尋功能的互動式的文字。這個管線使得使用者可以在列出的目錄內容比螢幕長時目錄上下翻頁。

以 less 結束的管道（或 more，這是個相似的分頁工具）是最常被使用的。這讓使用者可以閱覽尚未顯示的大量文字（受可用快取限制，控制檯的螢幕大小、螢幕快取大小往往有限，不足以一次先輸出所有輸出內容，也不能自由滾動內容），若少了這工具則這些文字將會捲過終端機而無法閱讀到。換句話說，他們將程式設計師從為自己的軟體開發分頁器的負擔中解放了出來：他們只需要把他們的輸出用過 “管道” 匯入到 less 程式中即可，甚至也可以完全不顧分頁問題，去假定他們的使用者會在需要將輸出分頁的時候自己去這樣做。

1.2 複雜樣例

以下是一個管線的範例，執行由一 URL 標示的全球資訊網資源的一種拼寫檢查器。之後是關於這個其作用的說明。注意「\」是用來把這六行轉為一個命令列。

curl "https://en.wikipedia.org/wiki/Pipeline_(Unix)" | \

sed's/^a-zA-Z //g' | \

tr 'A-Z ' 'a-z\n' | \

grep 'a-z' | \

sort -u | \

comm -23 - /usr/share/dict/words | \

less

curl 取得該網頁的 HTML 內容（在有些系統上可以使用 wget）。
sed 移除非空格的字元和網頁內容的字母，並以空格取代之。
tr 把大寫字母改成小寫字母，並把行列裡的空格換成新行（每個詞現在各佔有獨立的一行）。
grep 過濾得到那些至少有一個小寫字母的行（刪除空行）。
將 “單詞”（也就是每一個行）按照字母順序排序，並且透過命令列的 - u 引數來刪除重複的行。
查詢兩個檔案中的共同行，-23 過濾掉只有第二個檔案擁有的行、兩個檔案共有的行，僅僅留下只在第一個檔案中有的行。在檔名的位置上的 - 引數表示要求 comm 使用標準輸入（在這個例子裡，他的標準輸入來自於管道上游的標準輸出）作為輸入，而不是以普通檔案作為輸入。最終得到一串沒有出現在 /usr/share/dict/words 之中的 “單詞”（也就是一行）。
less 允許使用者翻頁瀏覽結果。

這個特殊的 “|” 字元告訴命令列直譯器（Shell）將前一個命令的輸出透過 “管道” 匯入到接下來的一行命令作為輸入。也就是說，curl 命令的輸出被作為 sed 命令的輸入，後面的命令也是這樣。

命令列介面中的管線

所有廣泛應用於 UNIX 和 Windows 中的 shell 程式都有特殊的語法構建管線。典型語法是使用 ASCII 中的垂直線 “|”（正是由於這個原因，這個符號常被稱為管道符）。當出現這樣的語法時 shell 會啟動各個程序，並調整各個程序的標準流之間的連線（還包括安排一些快取）。

2.1 錯誤流

通常，管線中的程序的標準錯誤流 ("stderr") 不會透過管道傳輸；它們被合併輸出到控制檯。然而，很多 Shell 提供一些擴充的語法去改變這一行為。比如在 csh Shell 和 bash 中，使用 “|&” 代替 “|” 來表示錯誤流也需要被合併進入標準輸出，並傳遞給下一個程序。Bourne shell 也可以合併錯誤流，透過 2>&1 也可以將錯誤流重定向到一個不同的檔案。

2.2 Pipemill

在一些常用的簡單管線中，shell 僅僅只是用管道來連線每個子程序，然後在子程序中執行外部命令。因此 shell 本身沒有透過管線來處理資料。

然而，shell 也有可能直接處理管線資料。構建這樣的語法像這樣：

command | while read var1 var2 ...; do

process each line, using variables as parsed into $var1, $var2, etc
(note that this is a subshell: var1, var2 etc will not be available
after the while loop terminates)

done

... 這樣的語法叫 "pipemill" 。

在程式中創造管道

3.1 匿名管道

使用 C 語言在 UNIX 中使用 pipe (2) 系統呼叫時，這個函式會讓系統構建一個匿名管道，這樣在程序中就打開了兩個新的，開啟的檔案描述符：一個只讀端和一個只寫端。管道的兩端是兩個普通的，匿名的檔案描述符，這就讓其他程序無法連線該管道。

為了避免死鎖並利用程序的並行執行的好處，有一個或多個管道的 UNIX 程序通常會呼叫 fork (2) 產生新程序。並且每個子程序在開始讀或寫管道之前都會關掉不會用到的管道端。或者程序會產生一個子執行緒並使用管道來讓執行緒進行資料交換。

實現程式碼：

include

int

main (int argc, char *argv)

{

int pipefd2;

pid_t cpid;

char buf;

if (argc != 2) {fprintf (stderr, "Usage: % s \n", argv0);

exit (EXIT_FAILURE);

}

if (pipe (pipefd) == -1) {perror ("pipe");

exit (EXIT_FAILURE);

}

cpid = fork ;

if (cpid == -1) {perror ("fork");

exit (EXIT_FAILURE);

}

if (cpid == 0) { / Child reads from pipe /

close (pipefd1); / Close unused write end /

while (read (pipefd0, &buf, 1) > 0)

write (STDOUT_FILENO, &buf, 1);

write (STDOUT_FILENO, "\n", 1);

close (pipefd0);

exit (EXITSUCCESS);

} else { / Parent writes argv1 to pipe /

close (pipefd0); / Close unused read end /

write (pipefd1, argv1, strlen (argv1));

close (pipefd1); / Reader will see EOF /

wait (NULL); / Wait for child /

exit (EXIT_SUCCESS);

}

}

3.2 具名管道

具名管道可以透過呼叫 mkfifo (2) 或 mknod (2) 來構建，當被呼叫時表現為輸入或輸出的檔案。這樣可以允許建立多個管道，並且將其同標準錯誤重定向或 tee 結合起來使用更為有效。

實現程式碼：

include

int main (int argc, char *argv) {

char filename = "test_fifo";

if (!mkfifo (filename,SIRUSR | SIWUSR| SIRGRP|SIWGRP)){pid_t pid = fork ;

if (pid == 0){	//child

int fd = open (filename, O_WRONLY);

if (fd 0){	//father

int fd = open (filename, O_RDONLY);

if (fd

以上程式碼在編譯後執行時給出一個引數，子程序會將該引數內容寫入管道（該管道在當前目錄下，檔名為 “test_fifo”），父程序從管道中讀取內容並顯示出來

實現

在大多數類 UNIX 作業系統中，管線上的所有程序同時啟動，輸入輸出流也已經被正確地連線，並且這些程序被排程程式所管理。最為重要的一點就是，所有的 UNIX 管道和其他管道實現不一樣的地方就是快取的概念：輸出程序可能會以每秒 5000 byte 的速度輸出，但是接收程序也許每秒只能接收 100 byte，但不會有資料丟失。原因就是管道上游的程序的所有輸出都會被放入一個佇列中。當下遊進程開始接收資料時，作業系統就會將資料從佇列傳至接收程序，並將傳完的資料從佇列中移除。當快取佇列空間不足時，上游程序會被終止，直到接收程序讀取資料為上游程序騰出空間。在 Linux 中，快取佇列的大小是 65536 byte。

4.1 網路管線

根據 Unix 哲學 ——“一切都是檔案”，netcat 和 socat 這樣的工具可以將管道連線到 TCP/IP 套接字。

歷史

管道的概念以及垂直線的記號 (|) 都是由道格拉斯・麥克羅伊發明的，他是早期命令列外殼的作者。他發現他常常將一個程式的輸出作為另一個程式的輸入，於是便發明了 “管道。它的想法在 1973 年被實現，Ken Thompson 將管道新增到了 UNIX 作業系統。這個點子最終被移植到了其他的作業系統，比如 DOS、OS/2、Microsoft Windows 和 BeOS，而且常常使用相同的記號 (垂直線)。

雖然管道概念是獨立發展的，但是 Unix 管道相似於、也確實晚於由 Ken Lochner 在 20 世紀 60 年代為開發的 'communication files'。

在蘋果 Automator（類似管道一樣將多個重複的命令連結起來）的那個機器人拿著一根管子的圖示也是對於最初 Unix 管道概念的紀念。

5.1 其他作業系統