在shell中咱們常常用到管道,有沒考慮過Shell是怎麼實現管道的呢?html
cat minicom.log | grep "error"
咱們知道,每個進程都有3個標準的輸入輸出文件描述符shell
描述符編號 | 簡介 | 做用 |
---|---|---|
0 | 標準輸入 | 通用於獲取輸入的文件描述符 |
1 | 標準輸出 | 通用輸出普通訊息的文件描述符 |
2 | 標準錯誤 | 通用輸出錯誤信息的文件描述符 |
咱們還知道,系統調用pipe
能夠建立無名管道bash
int pipe(int pipefd[2]);
pipe
的做用是建立無名管道,並建立兩個文件描述符code
文件描述符 | 做用 |
---|---|
pipefd[0] | 管道數據出口 |
pipefd[1] | 管道數據入口 |
在上文的基礎上,咱們再看看Shell如何實現管道的。htm
Shell中經過fork
+exec
建立子進程來執行命令。若是是含管道的Shell命令,則管道先後的命令分別由不一樣的進程執行,而後經過管道把兩個進程的標準輸入輸出鏈接起來,就實現了管道。blog
例如進程
grep "error" minicom.log | awk '{print $1}'
這句命名的做用很是簡單,ip
grep
命令在minicom.log中檢索含有error
關鍵字的行awk
命令打印grep
的輸出結果中每一行的第一個字段在Shell中要實現這樣的效果,有4個步驟:get
這樣就實現了Shell管道:grep
把結果輸出到管道,awk
從管道獲取數據cmd
我沒研究過Shell的代碼,但不妨礙咱們從功能倒推實現,若是是我,我會怎麼作呢?
int main(int argc, char **argv) { while(1) { int pfds[2]; pid_t cmd1, cmd2; if ((cmd1 = fork()) < 0) { ... } else if (cmd1 == 0) { /* child */ /* * dup2 把 pfds[1](管道數據入口描述符) 複製到 文件描述符1&2 * 實現把cmd1的標準輸出和標準錯誤 輸送到管道 */ dup2(pfds[1], STDOUT_FILENO); dup2(pfds[1], STDERR_FILENO); close(pfds[0]); close(pfds[1]); exec(cmd1...); __exit(127); } if ((cmd2 = fork()) < 0) { ... } else if (cmd2 == 0) { /* child */ /* * dup2 把 pfds[0](管道數據出口描述符) 複製到 文件描述符0 * 實現cmd2從管道中讀取(cmd1的輸出)數據 */ dup2(pfds[0], STDIN_FILENO); close(pfds[0]); close(pfds[1]); exec(cmd2...); __exit(127); } close(pfds[0]); close(pfds[1]); wait(...); } }