管道 (Unix)

文字終端機上一個包含三個程式的管道
在類Unix操作系統（以及一些其他借用了這個設計的操作系統，如Windows）中，管道（Pipeline）是一系列將標準輸入輸出鏈接起來的進程，其中每一個進程的輸出被直接作為下一個進程的輸入。 每一個鏈接都由匿名管道實現。管道中的組成元素也被稱作Filter_(software)。
這個概念是由道格拉斯·麥克羅伊為Unix 命令行發明的，因與物理上的管道相似而得名。

**例子**

**簡單樣例**
ls -l | less
在這個例子中，ls用於在Unix下列出目錄內容，less是一個有搜索功能的交互式的文本分頁器。這個管線使得用戶可以在列出的目錄內容比屏幕長時目錄上下翻頁。
以less結束的管道（或more，這是個相似的分頁工具）是最常被使用的。這讓使用者可以閱覽尚未顯示的大量文字（受可用緩存限制，控制檯的屏幕大小、屏幕緩存大小往往有限，不足以一次先輸出所有輸出內容，也不能自由滾動內容），若少了這工具則這些文字將會捲過終端機而無法閱讀到。換句話說，他們將程序員從為自己的軟件開發分頁器的負擔中解放了出來：他們只需要把他們的輸出用過“管道”導入到less程序中即可，甚至也可以完全不顧分頁問題，去假定他們的用戶會在需要將輸出分頁的時候自己去這樣做。

**複雜樣例**
以下是一個管線的範例，執行由一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 過濾得到那些至少有一個小寫字母的行（刪除空行）。
# sort 將“單詞”（也就是每一個行）按照字母順序排序，並且通過命令行的-u參數來刪除重複的行。
# comm 查找兩個文件中的共同行，-23過濾掉只有第二個文件擁有的行、兩個文件共有的行，僅僅留下只在第一個文件中有的行。在文件名的位置上的-參數表示要求comm使用標準輸入（在這個例子裡，他的標準輸入來自於管道上游的標準輸出）作為輸入，而不是以普通文件作為輸入。最終得到一串沒有出現在/usr/share/dict/words之中的“單詞”（也就是一行）。
# less 允許用戶翻頁瀏覽結果。
這個特殊的“|”字符告訴命令行解釋器（Shell）將前一個命令的輸出通過“管道”導入到接下來的一行命令作為輸入。也就是說，curl命令的輸出被作為sed命令的輸入，後面的命令也是這樣。

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

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

**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" 。

**在程序中創造管道**

**匿名管道**
使用C語言在UNIX中使用pipe(2)系統調用時，這個函數會讓系統構建一個匿名管道，這樣在進程中就打開了兩個新的，打開的文件描述符：一個只讀端和一個只寫端。管道的兩端是兩個普通的，匿名的文件描述符，這就讓其他進程無法連接該管道。
為了避免死鎖並利用進程的並行運行的好處，有一個或多個管道的UNIX進程通常會調用fork(2)產生新進程。並且每個子進程在開始讀或寫管道之前都會關掉不會用到的管道端。或者進程會產生一個子線程並使用管道來讓線程進行數據交換。
實現代碼：

#include 
#include 
#include 
#include 
#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(EXIT_SUCCESS);
} 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);
}
}

**具名管道**
具名管道可以通過調用mkfifo(2)或mknod(2)來構建，當被調用時表現為輸入或輸出的文件。這樣可以允許建立多個管道，並且將其同標準錯誤重定向或tee結合起來使用更為有效。
實現代碼：
#include 
#include 
#include 
#include  
#include  
#include 
#include 
int main(int argc, char *argv) {
char filename = "test_fifo";
if (!mkfifo(filename,S_IRUSR | S_IWUSR| S_IRGRP|S_IWGRP)){
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。

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

**歷史**
管道的概念以及垂直線的記號(|)都是由道格拉斯·麥克羅伊發明的，他是早期命令行外殼的作者。他發現他常常將一個程序的輸出作為另一個程序的輸入，於是便發明了“管道。它的想法在1973年被實現，Ken Thompson將管道添加到了UNIX操作系統。這個點子最終被移植到了其他的操作系統，比如DOS、OS/2、Microsoft Windows和BeOS，而且常常使用相同的記號(垂直線)。
雖然管道概念是獨立發展的，但是 Unix 管道相似於、也確實晚於由Ken Lochner在20世紀60年代為Dartmouth Time Sharing System開發的'communication files'。
在蘋果Automator（類似管道一樣將多個重複的命令鏈接起來）的那個機器人拿著一根管子的圖標也是對於最初Unix管道概念的紀念。

**其他作業系統**
其他作業系統的這個特色源自於Unix，例如 Taos 和 MS-DOS，最終成為軟體工程的管道與過濾器設計模式。

**參見**
 匿名管道，一個用於行程間通訊的 FIFO 架構。- GStreamer，一個基於管道的多媒體架構。- 哈特曼管道- 命名管道，用於進程間通信的持久性管道- 流水線 (計算機)，與計算機相關的其他管線- 管道 (軟件)，軟件管線的基本概念- 重定向 (計算機)- tee，將管線內容取出的一個通用程序- XML管道，處理XML的管線

**引用**
Sal Soghoian on MacBreak Episode 3 "Enter the Automatrix"

**外部鏈接**
  History of Unix pipe notation date=20150408054606 
**  Doug McIlroy’s original 1964 memo date=20201109000841 , proposing the concept of a pipe for the first time
*create an interprocess channel- Pipes: A Brief Introduction date=20200626204944  by The Linux Information Project (LINFO)- Unix Pipes – powerful and elegant programming paradigm (Softpanorama) date=20210225083402 - Ad Hoc Data Analysis From The Unix Command Line at Wikibooks date=20210308123110  – Shows how to use pipelines composed of simple filters to do complex data analysis.- Use And Abuse Of Pipes With Audio Data date=20190722124528  – Gives an introduction to using and abusing pipes with netcat, nettee and fifos to play audio across a network.- stackoverflow.com date=20210227165030  – A Q&A about bash pipeline handling.