管道和xargs命令

1. stdin,stdout,stderr
对于新生成的任何进程来讲,都可以使用stdin,stdout,stderr这些文件指针来访问标准输入,标准输出,错误文件。他们的类型都是FILE *,属于c运行库的类型。而内核则使用文件描述符来代表文件。STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO分别被定义为0,1,2。
Dup2( srcfd , destfd )的作用为将srcfd文件描述符复制一份,并且让destfd代表复制后的文件描述符。这样srcfd,destfd指向共同的file table entry,并拥有共同的inode。调用该函数时,destfd除了可以是普通的文件描述符之外,还可以是STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO的任意一个。这就位管道命令提供了很好的支持。
注:调用 dup2 时,如果 destfd 已经是一个有效的文件描述符,那么将首先关闭 destfd (调用 close 函数)
2. fork
fork函数可以生成子进程,并且子进程共享父进程的所有的文件描述符,数据,heap,stack。Child其实是parent的一份clone。事实上linux就是利用clone实现的fork。
把fork和dup2,pipe结合起来,就可以实现shell中支持的管道|了。
[1] 父进程首先调用pipe,返回fd0(用来从管道读数据),fd1(用来象管道写数据)
[2] 父进程调用fork生成子进程,此时子进程也共享到了父进程的fd0,fd1
[3] 子进程调用dup2( fd0 , STDIN_FILENO),这样就导致了子进程从stdin读取数据时,实际是从管道读取数据
[4] 子进程调用exec系列函数,执行相应的程序
[5] 父进程通过fd1向管道写数据
[6] 子进程通过stdin读取父进程向管道写入的数据。
其实,这就是shell执行管道的基本流程。

3. Unix哲学
一般的控制台程序都是从stdin读取参数或需要处理的数据,并把结果输出到stdout中。而unix的哲学是:
[1] Rule of Modularity: Write simple parts connected by clean interfaces
[2] Rule of Composition: Design programs to be connected to other programs
[3] Rule of Clarity: Clarity is better than cleverness
[4] Rule of Simplicity: Design for simplicity; add complexity only where you must
[5] Rule of Transparency: Design for visibility to make inspection and debugging easier
[6] Rule of Robustness: Robustness is the child of transparency and simplicity
[7] Rule of Least Surprise: In interface design, always do the least surprising thing
[8] Rule of Repair: When you must fail, fail noisily and as soon as possible
[9] Rule of Economy: Programmer time is expensive; conserve it in preference to machine time
[10] Rule of Generation: Avoid hand-hacking; write programs to write programs when you can
[11] Rule of Representation: Use smart data so program logic can be stupid and robust
[12] Rule of Separation: Separate policy from mechanism; separate interfaces from engines
[13] Rule of Optimization: Prototype before polishing. Get it working before you optimize it
[14] Rule of Diversity: Distrust all claims for “one true way”
[15] Rule of Extensibility: Design for the future, because it will be here sooner than you think
基于这些哲学,就导致了unix系统中有很多小的工具,每个工具的功能都很单一,并且这些工具可以任意组合,完成复杂的功能。比如find,grep,awk,xargs等等的组合。
这些功能组合是通过管道|来完成的,即前一个程序的输出,作为下一个程序的输入。
我们看看grep的命令行参数:
Usage: grep [OPTION]... PATTERN  [FILE] ...
Search for PATTERN in  each FILE or standard input.
Example: grep -i 'hello world'  menu.h main.c
我们可以看到,grep可以从多个文件中搜索字符串,并且也可以从stdin读取字符串并搜索。上例中,就是从menu.h main.c中搜索hello world。如果输入grep –i ‘hello’,并回车,然后grep将等待用户输入一行数据,并在该行数据中搜索hello,如果找到的话,就会输出该字符串。
$grep –i ‘hello’
Abcdefg
Hi hello
Hi hello
Stop
^D
$
其中蓝色的字符为用户输入的字符,红色的为grep找到匹配的字符串后,输出的字符。
 
我们可以猜到grep的实现,如果命令行的最后有一个文件名,或多个文件名,或路径的话,将从这些文件中读取内容,并匹配regex。否则的话,将从stdin读取数据,并匹配regex。
联系到上面所讲的pipe,fork,dup2函数,我们可以看到,这就是shell的基本执行过程。需要注意的是, grep 必须支持从 stdin 读取数据,否则管道就不能够实现了。当然,想支持管道操作的程序必须遵守这个规则。

4. Xargs
假设有这样一个需求,我们需要从整个文件系统中搜索字符串hello,我们写了如下的shell:
$grep –i ‘hello’  /*/*
这将导致命令行参数太多。而每个系统对于参数列表的大小都有限制。比如ARG_MAX一般至少定义为4096 bytes。如果超过了ARG_MAX,将产生shell错误:
Argument list too lang
为了避免这个问题可以使用xargs命令。他的格式为:
xargs [opt] [  command [initial-arguments] ]
其中opt是xargs本身的命令行参数。
他的作用为,build and execute command lines from standard input。他从stdin读取由空格分割的字符串(假设为arg0,arg1,… argN),并执行 command [initial-arguments] arg0 arg1 …argN ,如果参数太多的话, xargs 保证参数大小在不超过系统限制的 ARG_MAX bytes 大小的前提下,一次或多次执行 command [initial-arguments] 命令。比如执行了如下命令:
$find / -name ‘*.h’ | xargs grep –i ‘stdin’ | less
假设执行两次,第一次为 grep –i ‘stdin’ a1.h a2.h …  a3000.h | less
第二次为 grep –i ‘stdin’ a3001.h a3002.h … a4000.h | less
该命令的实际执行情况为:(推测)
Shell 执行 find xargs less 程序。 xargs 顺序执行了两次 grep 程序。
xargs从find的结果读取数据是很普通的,不需要额外的解释。
xargs同less的数据传递看起来有些麻烦,其实也挺简单的。 xargs stdin 读取管道的数据,并按照 ARG_MAX 为界限进行分割,执行 fork execv(“grep”) 一次或多次就可以了。因为 grep 使用普通的 printf 来输出结果,而这样的结果正好作为 less 的输入。因为 xargs grep 虽然存在父子关系,但是他们的 stdout 是同一个 stdout 。对于 less 程序来讲, grep 的输出和 xargs 的输出是没有区别的。
 
注意:
[1] XXX | grep –i ‘hello’
[2] XXX | xargs grep –i ‘hello’
[1] 的情况下, grep 将通过管道读取 XXXX 的输出结果,并在该结果中搜索 hello
[2] 的情况下, xargs 将通过管道读取 XXXX 的输出结果,并将该结果作为 grep 的最后的 FILE 参数,和 grep –i ‘hello’ 组合成完整的命令(如  grep –i ‘hello’ stdio.h stdlib.h )后,执行该命令。 grep stdio.h stdlib.h 文件中搜索 hello
两者对于管道的输出作不同的处理,前者是 grep 直接从 stdin 中读取管道数据,并搜索。后者是 xargs (他的命令行选项为  grep –i ‘hello’ 直接从 stdin 中读取管道数据,并和 grep –i ‘hello’ 组合,然后调用 exec 执行该命令。这种不同是体现在 grep xargs 对读取管道数据后采取的不同处理,和管道本身的机制没有关系。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值