在 UNIX/Linux 系统中,xargs 这个命令和其他的命令结合之后,将会变得非常有用。这里我们整理了一些常见的 xargs 使用范例与教学,透过这些简单的范例可以很快的了解 xargs 的各种使用方法。
虽然这里的范例都很简单,但是当你了解 xargs 的用法之后,你会发现 xargs 其实可以拿来处理各式各样的问题,而且非常好用,尤其是对于服务器的系统管理者而言,在管理大量的文件和目录结构时,有这样的工具会方便很多。
xargs 基本用法
xargs 这个命令会从标准输入(standard input)读取资料,并以空白字符或换行作为分隔,将输入的内容切割成多个字符串,并将这些字符串当成指定命令(预设为 /bin/echo)执行时的参数。如果直接执行
xargs
接着在终端中输入
arg1 arg2
arg3
结束时按下 Ctrl + d。这时候 xargs 就会把输入的两行资料切割成 3 个字串(分别为 arg1、arg2 与 arg3),接著把这三个字符串当作指定命令的参数。而这里因为我们没有指定任何命令,所以默认就会使用 /bin/echo,结果就是直接把三个字符串输出至终端中。
arg1 arg2 arg3
这里虽然输入的内容中有包含一个换行字符(\n),不过因为 xargs 默认不会保留换行字符,所以整个输出就变成一整行。这个效果相当于
/bin/echo arg1 arg2 arg3
xargs 在读取标准输入的内容时,会自动忽略空白行,多余的空白或是 tab 字符也会被自动忽略。
如果要指定 xargs 所要执行的命令,可以直接将命令的名称放在所有 xargs 的参数之后,例如
xargs cat
arg1 arg2
arg3
就相当于
cat arg1 arg2 arg3
分隔字符
如果要指定 xargs 在读取标准输入时所使用的分隔字符,可以使用 -d 这个参数。例如:
xargs -d\n
arg1 arg2
arg3
输出为
arg1 arg2
arg3
这里我们使用 -d 这个参数指定分隔字符时,xargs 就会将换行字符保留下来,所以输出就分成了两行。
参数个数上限
默认情况下,xargs 会把从标准输入的资料所分割出来的字串,一次全部都放进指定命令参数中,例如:
echo a b c d e f | xargs
就相当于
echo a b c d e f
所以输出为
a b c d e f
如果我们不想让所有的参数都放进一个命令中执行,那么可以使用 -n 参数来指定每一次执行命令所使用的参数个数上限值,例如
echo a b c d e f | xargs -n 3
就相当于
echo a b c echo d e f
所以输出就变为
a b c d e f
执行前的确认
如果你对于 xargs 的使用方式不是很熟悉,或是需要以 root 权限执行一些不容出错的命令,可以加上 -p 参数,让指令在实际执行之前先进行确认的动作。例如:
echo a b c d e f | xargs -p -n 3
则在每一行命令执行前,都会先确认,如果要执行就输入 y,若不执行则输入 n。假设每一个命令我们都输入 y 让它执行,则整个输出就会变成这样:
/bin/echo a b c ?…y /bin/echo d e f ?…a b c y d e f
这里因为 xargs 的确认信息与 echo 指令的输出混在一起,所以看起来有点奇怪。
如果所有的指令进行确认时,我们都输入 n,这样就不会执行任何的 echo 命令,输出就会像这样:
/bin/echo a b c ?…n /bin/echo d e f ?…n /bin/echo ?…n
忽略空字符串参数
大家应该有注意到上面的例子,当我们使用 -p 参数时,如果所有的命令都输入 n 跳过不执行时,最后还会出现一个没有任何参数的 echo 指令,如果想要避免以这种空字符串作为参数来执行指令,可以加上 -r 参数:
echo a b c d e f | xargs -p -n 3 -r
/bin/echo a b c ?…n
/bin/echo d e f ?…n
如果标准输入为空字符串时,xargs 默认还是会执行一次 echo 指令:
xargs -p
/bin/echo ?…n
这种状况同样可以加上 -r 参数:
xargs -p -r
显示执行的命令
使用 -t 参数可以让 xargs 在执行命令之前先显示要执行的指令,这样可以让使用者知道 xargs 执行了哪些命令。
xargs -t
abcd
按下 Ctrl + d 之后,xargs 就会先输出要执行的命令,接著才是实际执行的输出,最后得到输出为
/bin/echo abcd
abcd
xargs 与 find 的组合命令
xargs 本身的功能并不多,但是他跟其他的 Linux 命令一起搭配使用时,功能就会显得很强大。
与 find 合在一起使用是 xargs 的一项非常重要的功能,它可以让你找寻特定的文档,并且指定需要的操作。
假设我们有一些文件夹,其中包含各式各样的文件,以 tree 命令查看目录结构会呈现
. ├── folder1 │ ├── file1.c │ ├── file1.h │ ├── file2.c │ └── file2.h └── folder2 ├── file3.c └── file3.h
假设我们想要找出目前路径之下的所有 .c 文件,并且将其删除,就可以使用
find . -name "*.c" | xargs rm -f
这里我们将 xargs 要执行的命令指定为 rm -f,让 find 所找到的 .c 文件都当成 rm -f 的参数,所以这个命令的效果就相当于
rm -f ./folder2/file3.c ./folder1/file2.c ./folder1/file1.c
如果你对于命令的操作还不是很熟悉,也可以配合上面介绍的 -p 参数,在执行前确认一下
find . -name "*.c" | xargs -p rm -f
删除 .c 文件之后,以 tree 命令查看整个目录树就会变成
. ├── folder1 │ ├── file1.h │ └── file2.h └── folder2 └── file3.h
这里因为是示范用的例子,所以并没有建立太多的文件与目录结构。在实际的应用上,当整个目录结构很复杂、文件又很多的时候,使用这样的方式就会非常有效率。
包含空白的文件名称
假设我们的目录中有一些文件名中包含空白字符:
touch "G T Wang.c"
在这种状况下如果用上面 find 与 xargs 的方式会无法将其删除,原因在于当我们执行
find . -name "*.c" | xargs rm -f
的时候,xargs 所产生的命令其实为
rm -f ./G T Wang.c
因为文件名包含空白,所以这会造成 rm 命令无法正确删除该文件。
这个时候我们可以将 find 加上 -print0 参数,另外将 xargs 加上 -0 参数,改成这样
find . -name "*.c" -print0 | xargs -0 rm -rf
这样即可正确处理包含空白字符的文件名称了。
命令列长度的限制
使用 –show-limits 参数可以查看系统对于命令列长度的限制,这些限制会跟 xargs 的运作情况有关,如果要处理很大量的资料时,这些限制要注意一下。
xargs --show-limits
输出为
Your environment variables take up 2022 bytes POSIX upper limit on argument length (this system): 2093082 POSIX smallest allowable upper limit on argument length (all systems): 4096 Maximum length of command we could actually use: 2091060 Size of command buffer we are actually using: 131072 Execution of xargs will continue now, and it will try to read its input and run commands; if this is not what you wanted to happen, please type the end-of-file keystroke. Warning: /bin/echo will be run at least once. If you do not want that to happen, then press the interrupt keystroke.
xargs 与 grep 的组合命令
xargs 与 grep 两个命令的组合也是一个很常见的使用方式,它可以让你找到特定文件后,再搜索文件里的内容。
假设我们要在所有的 .c 文件中找出 stdlib.h 这个字符串,就可以使用
find . -name '*.c' | xargs grep 'stdlib.h'
我直接拿 VTK 的原码来测试,输出会像这样
./CMake/TestOggTheoraSubsampling.c:#include ./Wrapping/Tools/lex.yy.c:#include ./Wrapping/Tools/vtkWrapPythonInit.c:#include ./Wrapping/Tools/vtkWrapTcl.c:#include ./Wrapping/Tools/vtkWrapTclInit.c:#include ./Wrapping/Tools/vtkParsePreprocess.c:#include ./Wrapping/Tools/vtkWrapText.c:#include ./Wrapping/Tools/vtkParseData.c:#include ./Wrapping/Tools/vtkParseHierarchy.c:#include ./Wrapping/Tools/vtkParseMain.c:#include
好了,这个就是xargs的基本使用,有不正确的欢迎补充。