10 November 2013

记得以前想要统计一个目录下的c代码行数,c代码分布在这个目录的各个子目录下,于是想用wc这个工具,但是wc又不能进入子目录去统计那些c代码,黔驴技穷,竟然还自己写了一个小程序来统计代码行数,后来才知道linux下一行命令就能解决:

find . -name "*.c" | xargs wc -l

好吧,引入今天要讲的主题,神奇工具——xargs。xargs是linux下一个很有用的工具,它将输入数据转换成命令行参数传给特定命令。那么上面那行代码就很容易解释,find命令找到的c代码经过管道传给xargs,xargs通过指定的转换将其作为命令行传给wc命令完成统计。

先看看最简单的用法:假设file文件中的内容是:

cat file
1
2
3
4

我们输入:

cat file | xargs
1 2 3 4

可以看到输出是1 2 3 4,因为xargs在默认情况下,用空格代替换行符。我们可以输入参数:

cat file | xargs -n 2 1 2 3 4

-n加上数字表示每行输出内容的数量。我们还可以指定字符作为分隔符:

echo "123x123x123" | xargs -d x
123 123 123

上例中,我们使用x作为分隔符。

ok简单用法说完了,我们来看看复杂点的用法。我们编写一段代码如下:

cecho.sh
#!/bin/bash

echo $*'#'

./cecho.sh 1 2 3
1 2 3#

上例中的bash代码实际上就是列出命令行参数并在最后加上“#”,如果我们想每行显示一个数字并在最后加上“#”应该怎么做?当然最简单的做法是:

./cecho.sh 1
./cecho.sh 2
./cecho.sh 3

其实使用xargs可以更为简单和便捷的实现这个过程。先把这些参数写到一个文件中,如下:

cat args.txt
1
2
3

输入如下命令:

cat args.txt | xargs -n 1 ./cecho.sh
1#
2#
3#

很明显,这种方法更便捷,更人性化,其用法和上面例子相同。假设:

./cecho.sh -p arg -l
-p arg -l#

-p和-l都是不可变的参数,只有arg可变,如果我们要实现一行一行显示并在结尾加上“#”,依然可以使用xargs工具:

cat args.txt | xargs -I {} ./cecho.sh -p {} -l

-I {}指定了替换字符串,也就是说,xargs -I {} 从文件中读取参数,并将这些参数替换到{}位置。怎么样,是不是感觉特别神奇,特别人性化。有了xargs工具,很多复杂的问题都被简化了。再看之前的命令:

find . -name "*.c" -print | xargs wc -l

这个命令有一个严重的问题,比如“hello world.txt”是某文件名,xargs会把这个文件解释为“hello”和“world.txt”两个文件。这时候我们应该如下这么做:

find . -name "*.c" -print0 | xargs -0 wc -l 

“-print0”以‘\0’为分隔符,而“xargs -0”也会以‘\0’作为分隔符。这样就避免了上面出现的问题。

也许你会对xargs嗤之以鼻,感觉它没有那么有用。事实上,当数据很大时,xargs工具才真正发挥它的作用。试想统计代码这个问题,如果你的目录下有非常非常多的文件,并且有很深的子目录,那么使用其他方法统计将会是繁琐甚至艰难的工作,xargs将这些工作简化,使其更人性化。



blog comments powered by Disqus