我们经常会看到 xargs 命令与 find 命令一起使用。find 命令提供文件名列表,xargs 命令可以让我们逐个使用这些文件名,把它们当作是另一个命令的输入一样使用。
什么是 xargs 命令
xargs命令从标准输入或另一个命令的输出中读取文本行,并将其转换为命令并执行。
我们经常会看到 xargs 命令与 find 命令一起使用。find 命令提供文件名列表,xargs 命令可以让我们逐个使用这些文件名,把它们当作是另一个命令的输入一样使用。
由于 rargs 会处理重定向,所以你需要提前了解关于标准输入、输出以及管道重定向相关的知识。关于管道重定向,可以参考我们之前的文章。
怎样使用 xargs 命令
xargs 命令的语法如下:
xargs [options] [command [initial-arguments]]
但一般我们不这样用,它的一个重要功能是将一个命令的输出组合到另一个命令中。我们看一个例子:
假如在当前路径下有一些txt文件,以各种鲜花名称命名,然后还有一个flowers.txt,记录了所有这些txt文件的名称:
[gliu@fedora work]$ ls
flowers.txt lily.txt one_lotus.txt rose.txt three_lotus.txt two_lotus.txt
[gliu@fedora work]$ cat flowers.txt
lily.txt
one_lotus.txt
rose.txt
three_lotus.txt
two_lotus.txt
现在我们的目标是查看 flowers.txt 中提到的所有文件的文件大小。根据以往的经验,我们可以使用 cat 命令来显示所有文件名,然后通过管道将其传输到 du 命令来检查文件大小。
但是如果我们直接使用管道的话,它不会给出 flowers.txt 中提到的每个文件的大小:
[gliu@fedora work]$ du -h
52K.
[gliu@fedora work]$ cat flowers.txt | du -h
52K.
为什么呢?首先,du 命令不接受标准输入;其次,cat 命令的输出不是单个的文件名,而是由换行符隔开的一个文本。
而 xargs 命令的神奇之处在于,它将把这个由空格或换行符分割的文本转换为下一个命令的单独输入。
[gliu@fedora work]$ cat flowers.txt | xargs du -h
4.0Klily.txt
4.0Kone_lotus.txt
16Krose.txt
4.0Kthree_lotus.txt
16Ktwo_lotus.txt
这相当于将这些文件名提供给 du 命令:
[gliu@fedora work]$ du -h lily.txt one_lotus.txt rose.txt three_lotus.txt two_lotus.txt
4.0Klily.txt
4.0Kone_lotus.txt
16Krose.txt
4.0Kthree_lotus.txt
16Ktwo_lotus.txt
怎么样,意识到 xargs 命令的强大了吗?
xargs 和 find:为彼此而存在
xargs 命令经常和 find 命令结合使用。
find 命令搜索文件和目录并返回它们的名称,有了 xargs 命令,你就可以将 find 命令的结果用于特定目的,如重命名、移动、删除等等。
比如,我们要搜索所有包含 red 一词的txt文件,可以在 xargs 的帮助下结合 find 和
grep 命令:
[gliu@fedora work]$ find . -type f -name "*.txt" | xargs grep -l red
./three_lotus.txt
./two_lotus.txt
./rose.txt
find 与 exec 命令组合的工作原理类似。不过我们今天只集中讨论 xargs 命令。
处理文件名中带有空格的文件
如果文件名中有空格,会稍微麻烦点。比如我们将上面的文件three_lotus.txt重命名为 three lotus.txt,那么当使用 xargs 处理时,会被认为是两个独立的文件,three 和 lotus.txt:
[gliu@fedora work]$ find . -type f -name "*.txt" | xargs grep -l red
./two_lotus.txt
grep: ./three: No such file or directory
grep: lotus.txt: No such file or directory
./rose.txt
在这种情况下,可以使用 find 命令的 -print0 选项,它使用 ASCII null 字符来换行,而不是换行符;同时,xargs 命令也需要带 -0 选项。
[gliu@fedora work]$ find . -type f -print0 -name "*.txt" | xargs -0 grep -l red
./two_lotus.txt
./three lotus.txt
./rose.txt
查看正在执行的命令
xargs 命令的 -t 选项,会打印正在执行的实际命令,所以可以用来查看正在执行的命令。
[gliu@fedora work]$ find . -type f -name "*.txt" | xargs -t touch
touch ./three_lotus.txt ./two_lotus.txt ./lily.txt ./rose.txt
在运行命令之前,强制 xargs 提示确认
有些情况需要格外小心,比如删除文件,最好看看要执行什么命令,必要的话可以选择拒绝执行。
可以使用 -p 选项,在执行前进行确认:
[gliu@fedora work]$ find . -type f -name "*.txt" | xargs -p rm
rm ./three_lotus.txt ./two_lotus.txt ./lily.txt ./rose.txt ?...n
结合占位符的使用
默认情况下,xargs命令将标准输入作为参数添加到命令末尾。当需要在最后一个参数之前使用时,这会产生问题。
比如,使用 move 命令,首先需要一个源,然后需要一个目标作为参数;如果要将找到的文件移动的目标文件,那么将不起作用:
[gliu@fedora work]$ find . -type f -name "*.txt" | xargs -p mv new_dir
mv new_dir ./three_lotus.txt ./two_lotus.txt ./lily.txt ./rose.txt ?...y
mv: target './rose.txt' is not a directory
这时候,可以用 -l 选项来使用占位符:
[gliu@fedora work]$ find . -type f -name "*.txt" | xargs -p -I {} mv {} new_dir
mv ./three_lotus.txt new_dir ?...n
mv ./two_lotus.txt new_dir ?...n
mv ./lily.txt new_dir ?...n
mv ./rose.txt new_dir ?...n
上述命令中,xargs 从 find 命令中获取所有的文件名,将其保存在 {} 中,然后转到 mv 命令并提供 {} 中的内容。
这里有一个主要的区别,它不是将所有文件名放在同一个命令中,而是逐个添加。这就是每个参数都会单独调用 mv 命令的原因。
注:上述命令中使用 {} 作为占位符,你可以使用其他字符作为占位符。{} 是安全的,易于理解和区分。
使用xargs运行多个命令
可以使用 xargs 的占位符来运行多个命令:
[gliu@fedora work]$ find . -type f -name "*.txt" | xargs -I {} sh -c 'ls -l {}; du -h {}'
-rw-rw-r-- 1 gliu gliu 0 May 28 17:02 ./three_lotus.txt
0./three_lotus.txt
-rw-rw-r-- 1 gliu gliu 0 May 28 17:02 ./two_lotus.txt
0./two_lotus.txt
-rw-rw-r-- 1 gliu gliu 0 May 28 17:02 ./lily.txt
0./lily.txt
-rw-rw-r-- 1 gliu gliu 0 May 28 17:02 ./rose.txt
0./rose.txt
这里需要注意下,占位符不会扩展到下一个管道重定向或者其他命令,这就是上述命令中为什么会使用 sh 命令的原因。
本文主要介绍了常用的 find 和 xargs 命令的使用,但 xargs 命令不是仅限于和find一起用。xargs 命令的一个很实际的例子是当要停止所有正在运行的 docker 容器时:
docker ps -q | xargs docker stop
与其他 Linux 命令一样,xargs 也有很多选项。关于详细信息,大家可以查阅 xargs 命令的 man 手册。