管道 (Unix)

在類Unix作業系統是一系列將標準輸入輸出連結起來的程序，其中每一個程序的輸出被直接作為下一個程序的輸入。 每一個連結都由匿名管道實現#重定向這個概念是由道格拉斯·麥克羅伊為Unix 命令列發明的，因與物理上的管道相似而得名。例子.簡單樣例.ls -l | less在這個例子中，codice_1用於在Unix下列出目錄內容，codice_2是一個有搜尋功能的互動式的文字。這個管線使得使用者可以在列出的目錄內容比螢幕長時目錄上下翻頁。以codice_2結束的管道，若少了這工具則這些文字將會捲過終端機而無法閱讀到。換句話說，他們將程式設計師從為自己的軟體開發分頁器的負擔中解放了出來：他們只需要把他們的輸出用過“管道”匯入到codice_2程式中即可，甚至也可以完全不顧分頁問題，去假定他們的使用者會在需要將輸出分頁的時候自己去這樣做。複雜樣例.以下是一個管線的範例，執行由一URL標示的全球資訊網資源的一種拼寫檢查器。之後是關於這個其作用的說明。注意「\」是用來把這六行轉為一個命令列。curl "http://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命令的輸出被作為sed命令的輸入，後面的命令也是這樣。命令列介面中的管線.所有廣泛應用於UNIX和Windows中的shell程式都有特殊的語法構建管線。典型語法是使用ASCII中的垂直線“|”。錯誤流.通常，管線中的程序的標準錯誤流("stderr")不會透過管道傳輸；它們被合併輸出到控制檯。然而，很多Shell提供一些擴充的語法去改變這一行為。比如在csh Shell和bash中，使用“|&amp;”代替“|”來表示錯誤流也需要被合併進入標準輸出，並傳遞給下一個程序。Bourne shell也可以合併錯誤流，透過 2&gt;&amp;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程序通常會呼叫codice_5產生新程序。並且每個子程序在開始讀或寫管道之前都會關掉不會用到的管道端。或者程序會產生一個子執行緒並使用管道來讓執行緒進行資料交換。實現程式碼：intmain(int argc, char *argv[])int pipefd[2];pid_t cpid;char buf;if (argc != 2) {fprintf(stderr, "Usage: %s \n", argv[0]);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(pipefd[1]); /* Close unused write end */while (read(pipefd[0], &amp;buf, 1) &gt; 0)write(STDOUT_FILENO, &amp;buf, 1);write(STDOUT_FILENO, "\n", 1);close(pipefd[0]);_exit(EXIT_SUCCESS);} else { /* Parent writes argv[1] to pipe */close(pipefd[0]); /* Close unused read end */write(pipefd[1], argv[1], strlen(argv[1]));close(pipefd[1]); /* Reader will see EOF */wait(NULL); /* Wait for child */exit(EXIT_SUCCESS);具名管道.具名管道可以透過呼叫codice_6或codice_7來構建，當被呼叫時表現為輸入或輸出的檔案。這樣可以允許建立多個管道，並且將其同標準錯誤重定向或codice_8結合起來使用更為有效。實現程式碼：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){ //childint fd = open(filename, O_WRONLY);if (fd  0){ //fatherint fd = open(filename, O_RDONLY);if (fd &lt; 0)perror("father open()");else{char buffer[200];int readed = read(fd, buffer, 199);close(fd);buffer[readed] = '\0';printf("%s\n",buffer);elseperror("fork()");elseperror("mkfifo() error:");以上程式碼在編譯後執行時給出一個引數，子程序會將該引數內容寫入管道，父程序從管道中讀取內容並顯示出來實現.在大多數類UNIX作業系統中，管線上的所有程序同時啟動，輸入輸出流也已經被正確地連線，並且這些程序被排程程式所管理。最為重要的一點就是，所有的UNIX管道和其他管道實現不一樣的地方就是快取的概念：輸出程序可能會以每秒5000 byte的速度輸出，但是接收程序也許每秒只能接收100 byte，但不會有資料丟失。原因就是管道上游的程序的所有輸出都會被放入一個佇列中。當下遊進程開始接收資料時，作業系統就會將資料從佇列傳至接收程序，並將傳完的資料從佇列中移除。當快取佇列空間不足時，上游程序會被終止，直到接收程序讀取資料為上游程序騰出空間。在Linux中，快取佇列的大小是65536 byte。網路管線.根據Unix哲學——“一切都是檔案”，codice_9和codice_10這樣的工具可以將管道連線到TCP/IP套接字。歷史.管道的概念以及垂直線的記號(|)都是由道格拉斯·麥克羅伊發明的，他是早期命令列外殼的作者。他發現他常常將一個程式的輸出作為另一個程式的輸入，於是便發明了“管道。它的想法在1973年被實現，Ken Thompson將管道新增到了UNIX作業系統。這個點子最終被移植到了其他的作業系統，比如DOS、OS/2、Microsoft Windows和BeOS，而且常常使用相同的記號(垂直線)。雖然管道概念是獨立發展的，但是 Unix 管道相似於、也確實晚於由Ken Lochner在20世紀60年代為開發的'communication files'。在蘋果Automator的那個機器人拿著一根管子的圖示也是對於最初Unix管道概念的紀念。其他作業系統.其他作業系統的這個特色源自於Unix，例如 Taos 和 MS-DOS，最終成為軟體工程的管道與過濾器設計模式。引用.