何时使用Xargs
1. 概述
xargs (“eXtended ARGuments”的缩写)是 Unix 和大多数类 Unix 操作系统上的命令。它将来自标准输入 (STDIN) 的输入转换为命令的参数。
在这个简短的教程中,我们将了解如何使用xargs从标准输入构建和执行命令。我们将在示例中使用 BASH,因此可能与其他 shell 略有不同。
2. 一个典型的例子
有时,我们可能需要将一个命令的输出作为另一个命令的输入。一些命令可以将输入作为命令行参数或来自标准输入。
但是,还有其他一些方法,例如*cp 、rm 和echo 只能将输入作为参数。在这种情况下,我们可以使用xargs*将来自标准输入的输入转换为 arguments。
现在让我们看一个例子来说明这一点。
假设我们需要从此log目录中删除所有超过 7 天的日志文件:
$ ls -l ./log
-rw-rw-rw- 1 someone someone 3645 Nov 19 2019 file1.log
-rw-rw-rw- 1 someone someone 22176 Oct 25 2019 file2.log
-rw-rw-rw- 1 someone someone 82051 Oct 25 2019 file3.log
-rw-rw-rw- 1 someone someone 1932647 Nov 6 2019 file4.log
-rw-rw-rw- 1 someone someone 225464 Jul 4 09:09 file5.log
-rw-rw-rw- 1 someone someone 3733 Jul 4 09:09 file6.log
要查找超过 7 天的文件,我们将使用带有*-mtime*选项的find 命令:
$ find ./log -type f -mtime +7
./log/file1.log
./log/file2.log
./log/file3.log
./log/file4.log
现在,让我们使用管道运算符将find的输出作为rm的输入发送:
$find . -type f -mtime +7 | rm
rm: missing operand
Try 'rm --help' for more information.
这将打印一条错误消息,因为rm需要参数并且无法从 STDIN 读取它们。
列出日志目录仍然显示相同数量的文件,因为rm命令失败:
$ ls -l ./log
-rw-rw-rw- 1 someone someone 3645 Nov 19 2019 file1.log
-rw-rw-rw- 1 someone someone 22176 Oct 25 2019 file2.log
-rw-rw-rw- 1 someone someone 82051 Oct 25 2019 file3.log
-rw-rw-rw- 1 someone someone 1932647 Nov 6 2019 file4.log
-rw-rw-rw- 1 someone someone 225464 Jul 4 09:09 file5.log
-rw-rw-rw- 1 someone someone 3733 Jul 4 09:09 file6.log
相反,让我们将xargs与rm一起使用:
$ find . -type f -mtime +7 | xargs rm
$ ls -l ./log
-rw-rw-rw- 1 someone someone 225464 Jul 4 09:09 file5.log
-rw-rw-rw- 1 someone someone 3733 Jul 4 09:09 file6.log
现在rm删除超过 7 天的文件。
现在让我们看看xargs的其他常见场景。
3.其他常见用途
3.1. 限制每行输出
要找出xargs对find命令的输出做了什么,让我们在rm命令之前添加echo:
$ find . -type f -name "*.log" | xargs -n 1 echo rm
rm ./log/file5.log
rm ./log/file6.log
因为我们添加了*-n 1参数,所以xargs将每一行变成了它自己的命令。*
3.2. 执行前启用用户提示
要在执行前用 y(是)和 n(否)选项提示用户,让我们使用*-p*选项:
$ find ./log -type f -name "*.log" | xargs -p -n 1 rm
rm ./log/file5.log ?...y
rm ./log/file6.log ?...n
由于我们提供了*“y”*选项,file5.log现在已被删除:
$ ls -l ./log
-rw-rw-rw- 1 someone someone 3733 Jul 4 09:09 file6.log
3.3. 在特定位置插入参数
xargs命令提供了在命令行末尾以外的任意位置插入列出的参数的选项。
-I选项采用一个字符串,该字符串在命令执行之前**被替换为提供的输入。**虽然这个字符串可以是任何字符集,但通常选择“%”。
让我们将file6.log移动到backup目录:
$ find ./log -type f -name "*.log" | xargs -I % mv % backup/
$ ls -l ./log
total 0
$ ls -l ./backup/
-rw-rw-rw- 1 someone someone 3733 Jul 4 09:09 file6.log
3.4. 启用多进程使用
要 **并行化操作,*我们可以使用-P*选项指定在输入参数列表上执行命令时使用的并行进程数。
让我们用它来将一系列wav文件并行编码为mp3格式:
$find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 mp3 -V8
编码过程使用三个进程执行,因为指定了*-P 3 *。
4.处理名称中有特殊字符的文件
文件名可以包含特殊字符,例如单引号、双引号或空格。
让我们看看在传递给 xargs 时如何处理这些文件。
4.1. 文件名包含空格
让我们检查xargs是否将文件file 1.log传递给rm命令,尽管名称中有空格:
$ ls -l ./log
-rw-rw-rw- 1 someone someone 3645 Nov 19 2019 file 1.log
$ find ./log -type f | xargs rm
rm: cannot remove ‘./log/file’: No such file or directory
rm: cannot remove ‘1.log’: No such file or directory
如我们所见,xargs向rm命令发送两个参数*./log/file和1.log而不是单个参数./log/file 1.log*。当然,没有**名为file和1.log 的文件,**所以rm命令给出错误消息No such file or directory。
为了使xargs识别名称中包含空格的文件,我们将再次使用-I*选项。但是我们必须引用占位符:*
$ find ./log -type f | xargs -I % rm "%"
$ ls -l ./log
total 0
文件1.log现已删除。
4.2. 文件名包含引号
让我们从另一个例子开始:
$ find ./log -type f
./log/file"2.log
./log/file'1.log
我们创建了两个日志文件。文件file'1.log 的名称中有一个单引号,而文件2.log 的名称中有一个双引号字符。
接下来,让我们将find的输出传递给xargs命令并尝试删除这两个文件:
$ find ./log -type f | xargs rm
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
让我们看看引用xargs的*-I*选项的占位符是否有帮助:
$ find ./log -type f | xargs -I % rm '%'
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
$ find ./log -type f | xargs -I % rm "%"
xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
测试表明,如果文件名称中包含单引号或双引号字符,则xargs无法通过引用文件名来工作。
为了处理这个问题,xargs和将输出传递给xargs 的命令都必须使用空字符作为记录分隔符。
我们可以将选项*-print0传递给find*命令,要求它输出由空字符分隔的文件名,而不是默认的换行符。
在xargs命令端,由于默认的记录分隔符也是换行符,我们需要*-0*选项来使用空字符作为记录分隔符:
$ find ./log -type f -print0 | xargs -0 rm
$ ls -l ./log
total 0
上面的输出显示这两个文件已被删除。
但是请注意,某些命令不能使用空字符作为分隔符(例如*head 、tail 、ls 、 echo 、sed 和*wc )。在这些情况下,如果输出包含引号,则xargs*无法处理此类命令的输出。