当你执行以下命令时:
ps -ef | grep cron
你正在使用的shell会调用pipe()
函数创建一个FIFO(命名管道),然后它会fork()
(生成自身的运行副本)。这将创建一个新的子进程。这个新生成的子进程将关闭其标准输出文件描述符(fd 1),并将fd 1附加到父进程(您执行命令的shell)创建的管道的写入端。这是可能的,因为fork()
系统调用将为每个进程维护一个有效的打开文件描述符(在这种情况下是管道fd)。接着它将使用exec()
调用第一个(在您的例子中)出现在PATH
环境变量中的ps
命令。使用exec()
调用后,此进程将成为您执行的命令。
所以现在您有了一个具有子进程的shell进程,在您的情况下,子进程是带有-ef
属性的ps
命令。
此时,父进程(shell)再次进行fork()
。这个新生成的子进程close()
它的标准输入文件描述符(fd 0),并将fd 0附加到父进程(您执行命令的shell)创建的管道的读取端。
接着它将使用exec()
调用第一个(在您的例子中)出现在PATH
环境变量中的grep
命令。
现在,您有了具有两个子进程(兄弟)的shell进程,其中第一个是带有-ef
属性的ps
命令,第二个是带有cron
属性的grep
命令。管道的读取端附加到grep
命令的STDIN
上,写入端附加到ps
命令的STDOUT
上:即ps
命令的标准输出附加到grep
命令的标准输入。
由于ps
命令会在标准输出上发送每个运行进程的信息,而grep
命令会在其标准输入上获取与给定模式匹配的东西,所以您将得到对第一个问题的答案:
- shell运行:
ps -ef;
- shell运行:
grep cron;
ps
向grep
发送数据(甚至包含字符串"grep cron")
grep
从STDIN
匹配其搜索模式,并且由于您传递给grep
的"cron"属性,它匹配到了字符串"grep cron",因为在grep
开始执行时,"grep cron"是ps
返回的字符串之一。
当您执行:
ps -ef | grep '[c]ron'
属性传递给 grep 命令,指示它匹配包含 "c" 后跟 "ron" 的内容。这类似于第一个示例,但这将破坏由 ps 返回的匹配字符串,因为:
- shell 运行:
ps -ef;
- shell 运行:
grep [c]ron;
ps
发送数据(甚至包含字符串 grep [c]ron
)到 grep
;
grep
无法从标准输入流中匹配其搜索模式,因为找不到包含 "c" 后跟 "ron" 的字符串,但它已经找到了一个包含 "c" 后跟 "]ron" 的字符串。
GNU grep 没有任何字符串匹配限制,在某些平台上(例如 Solaris、HPUX 和 aix),字符串的限制由 "$COLUMN" 变量或终端屏幕宽度给出。
希望这个长答案可以澄清 shell 管道过程的一些问题。
提示:
ps -ef | grep cron | grep -v grep
sleep 3 | sleep 3 | sleep 3
的结果感到惊奇的;-) - Alfe