在Linux环境中,有时我们需要将多个命令连接起来执行,但可能会遇到某个命令不接受管道输入的情况。这时,xargs
命令就派上了用场。它可以接收一个命令的输出,并将其作为参数传递给另一个命令,从而实现命令之间的灵活连接。
所有标准的Linux实用工具都关联着三个基本数据流:标准输入流(stdin)、标准输出流(stdout)和标准错误流(stderr)。
这些数据流处理文本信息。我们通过文本将输入(stdin)发送给命令,命令的响应(stdout)也会以文本形式显示在终端窗口中。同样,错误消息(stderr)也会以文本形式输出到终端。
Linux和类Unix操作系统的一大优点是能够将一个命令的stdout输出通过管道直接传递给第二个命令的stdin输入。这样,第一个命令无需关心其输出是否直接显示在终端,而第二个命令也无需在意其输入是否来自键盘,从而实现命令的无缝连接。
尽管所有的Linux命令都具备这三个标准流,但并非所有命令都能够接受其他命令的标准输出作为其标准输入。这意味着我们无法通过管道直接向这些命令传递输入。
xargs
命令的出现正是为了解决这个问题。它能够利用标准数据流构建灵活的执行管道。通过使用xargs
,我们可以让诸如echo
、rm
和mkdir
等命令能够接受标准输入作为参数。
xargs
命令详解
xargs
命令可以接收管道输入,也可以接收来自文件的输入。它会将这些输入作为参数传递给指定的命令。如果没有明确指定要使用的命令,xargs
默认会使用echo
。
下面,我们通过一些示例来展示xargs
如何将多行输入转换为单行输出。
如果我们使用ls -1
(每行列出一个文件)选项,将会得到一个单列的文件名列表。
ls -1 ./*.sh
这个命令会列出当前目录下所有的shell脚本文件。
正如我们预期的,输出是一个单列的列表。如果我们将这个输出通过管道传递给xargs
,会得到什么结果呢?
ls -1 ./*.sh | xargs
结果显示,所有的文件名都被合并成了一行文本,输出到了终端窗口。
正是这种将多行输入转换为单行输出的能力,使得我们能够将xargs
的输出作为其他命令的参数。
xargs
与 wc
的结合使用
我们可以很方便地使用xargs
让wc
命令计算多个文件的单词数、字符数和行数。
ls *.page | xargs wc
执行过程如下:
ls
命令列出所有的*.page
文件,并将列表传递给xargs
。xargs
命令将这些文件名传递给wc
。wc
命令将接收到的文件名视为命令行参数进行处理。
最终,每个文件的统计信息以及总和都会被显示出来。
使用带确认的 xargs
我们可以使用-p
(交互式)选项,让xargs
在执行命令之前提示我们进行确认。
例如,如果我们通过xargs
将一串文件名传递给touch
命令,touch
会创建相应的文件。
echo 'one two three' | xargs -p touch
此时,将会显示即将执行的命令,xargs
会等待我们输入”y”或”Y”(表示确认)或”n”或”N”(表示取消)并按下回车键。
如果我们只是按下回车键,会被视为输入了”n”,命令将不会执行。只有当我们输入”y”或”Y”并按下回车键时,命令才会执行。
我们输入”y”并按下回车键,命令被执行。可以通过ls
命令来检查文件是否已经创建。
ls one two three
xargs
与多个命令的结合使用
通过使用-I
(初始参数)选项,我们可以将多个命令与xargs
结合使用。
这个选项定义了一个“替换字符串”。xargs
接收到的输入将会替换命令行中所有出现该替换字符串标记的地方。
我们先使用tree
命令查看当前目录的子目录。-d
(目录)选项会让tree
忽略文件,只显示目录。
tree -d
可以看到,存在一个名为“images”的子目录。
现在,假设我们有一个名为“directories.txt”的文件,其中包含我们想要创建的目录的名称。可以使用cat
命令查看其内容。
cat directories.txt
我们将使用该文件的内容作为xargs
的输入数据。我们要执行的命令如下:
cat directories.txt | xargs -I % sh -c 'echo %; mkdir %'
分解如下:
cat directory.txt |
:这会将directories.txt
文件的内容(所有新目录名称)传递给xargs
。xargs -I %
:这定义了一个带有标记“%”的“替换字符串”。sh -c
:这会启动一个新的子shell。-c
(命令)选项告诉shell从命令行读取命令。'echo %; mkdir %'
:每个“%”标记都会被xargs
传递的目录名称替换。echo
命令会打印目录名称,mkdir
命令则会创建目录。
目录名会逐个被列出。
我们可以再次使用tree
命令来验证目录是否已经创建。
tree -d
将文件复制到多个位置
我们可以使用xargs
将一个文件复制到多个位置。只需要传递多个目标目录作为输入参数给xargs
,并指定每次只传递一个参数给cp
命令即可。
通过使用-n
(最大数量)选项,我们可以指定每次传递给cp
命令的参数数量。将其设置为1,表示每次只传递一个目录。
我们还使用了cp
命令的-v
(详细)选项,以便显示复制过程。
echo ~/Backups/ ~/Documents/page-files/ | xargs -n 1 cp -v ./*.page
文件会被逐个复制到这两个目录。 cp
命令会报告每个文件的复制操作。
删除嵌套目录中的文件
如果文件名中包含空格或特殊字符(如换行符),xargs
可能无法正确解释。可以使用-0
(空终止符)选项来解决这个问题。这个选项告诉xargs
使用空字符作为文件名的分隔符。
在本例中,我们将使用find
命令。find
命令自身也具有处理文件名中的空格和特殊字符的选项,即-print0
(全名,空字符)选项。
find . -name "*.png" -type f -print0 | xargs -0 rm -v -rf "{}"
分解如下:
find . -name "*.png"
:find
命令将从当前目录(.
)开始搜索名称与“*.png”匹配的文件(-type f
)。-print0
:名称将使用空字符作为结尾,从而可以处理包含空格和特殊字符的文件名。xargs -0
:xargs
也会将文件名视为以空字符结尾,空格和特殊字符不再造成问题。rm -v -rf "{}"
:rm
命令会显示详细的删除过程(-v
)。它会递归地删除目录(-r
),并且不会提示确认直接删除文件(-f
)。"{}"
会被每个文件名替换。
将会搜索所有子目录,并删除所有匹配的文件。
删除嵌套目录
假设我们想要删除一组嵌套的子目录。可以通过tree
命令来查看它们的结构。
tree -d
find . -name "level_one" -type d -print0 | xargs -0 rm -v -rf "{}"
这个命令会使用find
命令在当前目录中递归搜索。搜索目标是名为“level_one”的目录。目录名称会被传递给rm
命令进行删除。
这个命令与之前命令的区别在于,搜索目标变成了顶层目录的名称,并且使用了-type d
选项来告诉find
命令只搜索目录,而不是文件。
每个被删除的目录名称都会被打印出来。我们可以使用tree
命令再次检查:
tree -d
所有嵌套的子目录都已经被删除。
删除除特定类型外的所有文件
我们可以使用find
、xargs
和rm
命令来删除除特定类型以外的所有文件。这种做法可能会有点反直觉,我们需要指定要保留的文件类型,而不是要删除的文件类型。
-not
选项会告诉find
命令返回与搜索模式不匹配的文件名称。我们再次使用-I
(初始参数)选项,这次定义的替换字符串标记是“{}”。它与我们之前使用的“%”标记作用相同。
find . -type f -not -name "*.sh" -print0 | xargs -0 -I {} rm -v {}
我们可以使用ls
命令进行检查。目录中剩下的文件都是匹配“*.sh”搜索模式的文件。
ls -l
使用 xargs
创建归档文件
我们可以使用find
命令搜索文件,并通过xargs
将它们传递给tar
命令,从而创建归档文件。
我们将会在当前目录中进行搜索。搜索模式为“*.page”,所以我们将会查找所有“.page”文件。
find ./ -name "*.page" -type f -print0 | xargs -0 tar -cvzf page_files.tar.gz
随着归档文件的创建,文件列表会按预期显示出来。
数据中介
有时,在将多个命令组合使用时,我们需要一个中间的桥梁。xargs
命令正是填补了那些能够输出信息和那些不直接接受信息的命令之间的空隙。
xargs
和find
命令都有大量的选项。建议您查阅它们的手册页以获取更多信息。