漫谈-exec与xargs
-exec与xargs常和find联合在一起使用,实现对文件的操作。
-exec是find的一个选项,而不是exec命令。对于find命令,其命令选项参数非常特别,不是常见的UNIX-style的单字母选项,而采用了由单词构成,专业点说是指示性选项(predicate),这构成了一种新的UNIX-style命令语法规范。
find为啥要加-exec,这又得谈下UNIX-sytle,UNIX特别强调命令的功能和魔法,喜欢用一个命令实现多个功能。所以,高手们也特中意于单行命令,尽管不惜用管道不断地把多个命令串起来。我想,开发find命令的大佬肯定是觉得单find到东西太简单了,体现不了自己UNIX-sytle的范儿,所以就加上-exec这个选项,对find到的东西做进一步的灵活操作。
BTW,多了解些UNIX-style,你就逐步认识融入UNIX文化,对各种命令有了从骨子上的认识,应用起来会更加得心应手。
xargs与-exec明显不同,xargs可是单个的命令,功能要更加强大。xargs从字面上理解为,args表示多个参数(arguments),有了多个参数,就可联想到参数列表。最前面的x表示执行或操作(eXecute)。综合起来,就是对参数列表进行操作。
简单地看,-exec是找到一个文件就执行一次命令操作,xargs则是先把所有文件都找齐,再统一进行命令操作。举个例子,对于要find到1000个文件进行grep操作,find会为找到的第一个文件fork出一个子进程,这个子进程再进行grep操作。这时,find主进程却在等待,等到fork出去的子进程grep操作完成了,再进行下一个文件的寻找。这是个很休闲的处理方式。xargs命令却不同,xargs是一次找齐了所有文件,再用一个grep命令一次操作搞定。至于xargs对超长的参数列表还要进行分组,就不细谈了。
xargs明显比-exec的效率要高出不少。举个实际例子PK,一见高下。
- [root@localhost ~]# time find /usr/include -type f -exec grep NFS_VERSION {} \;
- version NFS_VERSION {
- #define NFS_VERSION 2
- real 0m17.328s
- user 0m5.418s
- sys 0m8.421s
- [root@localhost ~]# time find /usr/include -type f|xargs grep NFS_VERSION
- /usr/include/rpcsvc/nfs_prot.x: version NFS_VERSION {
- /usr/include/rpcsvc/nfs_prot.h:#define NFS_VERSION 2
- real 0m0.095s
- user 0m0.031s
- sys 0m0.061s
看sys指标,8.421s vs 0.061s, 140倍的差距。
xargs很强大,但使用时若碰到命名不规范的文件命名,就会发生问题,比如"a b.txt",它会理解成"a"和"b.txt"2个文件,解决的方法是在find中加"-print0"选项(注意是数字0,find的选项可都是由单词构成的,所以是print0,一个复合词嘛),意思找到一个文件就在文件名加上空字符('\0'),做为文件分割符,然后还必须在xargs加上"-0"选项,表示前面find过来的文件参数列表时是用'\0'做为文件分割符的,这样就能正确地识别区分每个文件了。上例中,较规范的写法就变为:
- [root@localhost ~]# time find /usr/include -type f -print0 |xargs -0 grep NFS_VERSION
- /usr/include/rpcsvc/nfs_prot.x: version NFS_VERSION {
- /usr/include/rpcsvc/nfs_prot.h:#define NFS_VERSION 2
- real 0m0.102s
- user 0m0.036s
- sys 0m0.065s
这里面又揭示出一个矛盾,文件命名和对文件的识别操作问题之间的矛盾。UNIX-sytle可是对文件的命名规则放得相当宽松哦,搞得好像是没有规范,后续若遇到文件名的问题再想办法解决。大家会试问,为啥不先对文件命名进行严格规范,免得后续操作麻烦。但是,UNIX的一个核心理念就是文件,文件命名当然要放得开,这样就满足了不同的文件命名需求,至于文件名带来的麻烦,先不管,放在一边,以后谁碰到谁解决,这就是UNIX文化。