在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(...); } }