我们可以用管道将一个命令的stdout(标准输出)重定向到另一个命令的stdin(标准输入)。例如:
$cat foo.txt | grep “test”
但是,有些命令只能以命令行参数的形式接受收据,而无法通过stdin接受数据流。在这种情况下,我们没法用管道来提供那些只有通过命令行参数才能提供的数据。
只有另辟蹊径。xargs是一个很有用的命令,它擅长将标准输入数据转换成命令行参数。
单行命令是一个命令序列,各命令之间不使用分号,而是使用管道操作符进行连接。精心编写的单行命令可以更高效、更简捷地完成任务。就文本处理而言,需要具备扎实的理论和实践才能够写出适合的单行命令解决方法。xargs就是构建单行命令的重要组件之一。
xargs命令用该紧跟在管道操作符之后。它以标准输入作为主要的源数据流。
command | xargs
----->>
xargs命令能将stdin接收到的数据重新格式化,再将其作为参数提供给其他命令。
xargs如何格式化数据:
-d选项,指明定界符
-n选项,指明每行最大的参数数量n
范例:将多行输入转换成单行输出
只需将换行符移除,在用” ”(空格)进行替换,就可以实现。xargs默认将空格作为定界符。xargs没有指定参数时,默认能将换行符替换成空格。
$cat example.txt
1 2 3 4 5 6
7 8 9 10
11 12
$catexample.txt | xargs
1 2 3 4 5 6 7 8 9 10 11 12
范例:将单行输入转换成多行输出
$cat examole.txt | xargs -n 3
1 2 3
4 5 6
7 8 9
10 11 12
$echo “splitXsplitXsplitXsplit” | xargs -d X
split splitsplit split
$echo “splitXsplitXsplitXsplit” | xargs -d X –n 2
split split
split split
xargs的初衷是将参数列表转换成小块分段传给其他命令,以避免参数列表过长。
编制一个定制版的echo来更好的理解xargs。
#!/bin/bash
#myecho.sh
echo $*’#’
当参数被传递给myecho.sh后,它会将这些参数打印出来,并以#字符作为结尾。例如:
$./myecho.sh arg1 arg2
arg1 arg2#
看下面问题:
有一个包含参数列表的文件(每行一个参数)。
每次提供一个参数:
./myecho.sh arg1
./myecho.sh arg2
./myecho.sh arg3
每次提供多个参数:
./myecho.sh arg1 arg2
./myecho.sh arg3
一次性提供:
./myecho.sh arg1 arg2 arg3
上面的问题,也可以用xargs实现。我们将参数保存到args.txt。
$cat args.txt
arg1
arg2
arg3
$cat args.txt | xargs -n 1 ./myecho.sh
arg1#
arg2#
arg3#
通过#号,可以知道myecho.sh运行了3次。
$cat args.txt | xargs -n 2 ./myecho.sh
arg1 arg2#
arg3#
#catargs.txt | xargs ./myecho.sh
arg1 arg2 arg3#
xargs不加参数时,默认将换行符替换成空格。就好像是默认,为这种,从参数列表文件中获取参数提供方便服务的。(想想,如果没有这种默认服务,那么想要将拥有多行文本的args.txt中的参数一次性提取,你会怎么做?)
上面例子中,我们直接为myecho.sh提供参数。这些参数都源于args.txt文件。但实际上除了它们外,我们还需要一些固定不变的命令参数。思考下面这种命令格式:
./myecho.sh -p arg1 –l
在上面的命令执行过程中,arg1是唯一的可变文本,其余部分保持不变。我们应该从args.txt中读取参数,并按照下面的方式提供给命令:
./myecho.sh -p arg1 -l
./myecho.sh -p arg2 -l
./myecho.sh -p arg3 -l
xargs有一个选项-I,可以提供上面这种形式的命令执行序列。我们可以用-I执行一个替换字符串,这个字符串在xargs扩展时会被替换掉。当-I与xargs结合使用,对于每一个参数命令都会执行一次。
$cat args.txt | xargs -I {} ./myecho.sh -p {} -l
-p arg1 -l#
-p arg2 -l#
-p arg3 -l#
结合find使用xargs:
两者结合使用可以让任务变得更轻松。不过,人们通常却是以一种错误的组合方式使用它们。例如:
$find . –type f -name “*.txt” -print | xargs rm -f
这样做很危险。有时可能会删除不必要删除的文件。我们没法预测分隔find命令输出结果的定界符究竟是’\n’还是’ ‘(空格)。很多文件名中都可能会包含空格符,而xargs很可能会误认为它们是定界符(例如,hell text.txt会被xargs误认为hell和text.txt)。
只要我们把find的输出作为xargs的输入,就必须将-print0与find结合使用,以字符null来分隔输出。
用find匹配并列出所有.txt文件,然后用xargs将这些文件删除:
$find . –type f -name “*.txt” -print0 | xargs -0 rm -f
删除所有txt文件。xargs -0将\0作为输入定界符。
统计所有C程序文件的行数:
$find code_path -type -f -name “*.c” -print0 | xargs -0 wc -l