Linux xargs
xargs 可以将一个命令的输出作为参数发送给另一个命令。
在 Linux 中,所有标准的应用程序都有与之关联的三个数据流。分别是标准输入流(stdin),标准输出流(stdout)和标准错误流(stderr)。这些流通过文本来运行,使用文本将输入(stdin)发送到命令,然后响应(stdout)将会以文本形式显示在终端窗口上。错误消息也以文本的形式显示在终端窗口上(stderr)。
Linux 和类 Unix 操作系统的一大功能是可以将一个命令的标准输出流传递到另一个命令的标准输入流。第一个命令不会管它的输出是否写到了终端窗口,第二个命令也不会管它的输入是否来自键盘。
虽然所有 Linux 命令都有三个标准流,但是并不是所有命令都接受另一个命令的标准输出作为它的标准输入流的输入。因此无法通过管道将输入传给这些命令。
xargs是一个使用标准数据流构建执行管道的命令。通过使用xargs命令可以使 echormmkdir 等命令接受标准输入作为它们的参数。

xargs命令

xargs接受管道输入,也可以接受来自文件的输入。xargs 使用该输入作为指定的命令的参数。如果没有给xargs指定特定的命令,则默认使用 echoxargs 始终生成单行输出,即使输入的数据是多行的。
假如使用 ls-1(每行列出一个文件)选项,则会得到一列文件名称:

  1. $ ls -l ./*.pid

这一命令列出了当前目录中的 Shell 脚本文件。
image.png
如果将输出结果通过管道传递给 xargs ,会得到的效果

  1. $ ls -l ./*.pid | xargs

image.png
可以看出来,输出以一长串文本的形式写到了终端上。由此可见,xargs 可以将输出作为参数传递给其他命令。

xargs 用三种用法

  • 分隔数据,避免参数过长
  • 传递参数,从而组合多个命令
  • 处理不支持管道的命令

    Xargs命令参数

    1. -0, --null
    2. 如果输入的 stdin 含有特殊字符,例如反引号 `、反斜杠 \、空格等字符时,xargs 将它还原成一般字符。为默认选项
    3. -a, --arg-file=FILE
    4. 从指定的文件 FILE 中读取输入内容而不是从标准输入
    5. -d, --delimiter=DEL
    6. 指定 xargs 处理输入内容时的分隔符。xargs 处理输入内容默认是按空格和换行符作为分隔符,输出 arguments 时按空格分隔
    7. -E EOF_STR
    8. EOF_STR 是 end of file string,表示输入的结束
    9. -e, --eof[=EOF_STR]
    10. 作用等同于 -E 选项,与 -E 选项不同时,该选项不符合 POSIX 标准且 EOF_STR 不是强制的。如果没有 EOF_STR 则表示输入没有结束符
    11. -I REPLACE_STR
    12. 将 xargs 输出的每一项参数单独赋值给后面的命令,参数需要用指定的替代字符串 REPLACE_STR 代替。REPLACE_STR 可以使用 {} $ @ 等符号,其主要作用是当 xargs command 后有多个参数时,调整参数位置。例如备份以 txt 为后缀的文件:find . -name "*.txt" | xargs -I {} cp {} /tmp/{}.bak
    13. -i, --replace[=REPLACE_STR]
    14. 作用同 -I 选项,参数 REPLACE_STR 是可选的,缺省为 {}。建议使用 -I 选项,因为其符合 POSIX
    15. -L MAX_LINES
    16. 限定最大输入行数。隐含了 -x 选项
    17. -l, --max-lines[=MAX_LINES]
    18. 作用同 -L 选项,参数 MAX_LINES 是可选的,缺省为 1。建议使用 -L 选项,因为其符合 POSIX 标准
    19. -n, --max-args=MAX_ARGS
    20. 表示命令在执行的时候一次使用参数的最大个数
    21. -o, --open-tty
    22. 在执行命令之前,在子进程中重新打开stdin作为/dev/TTY。如果您希望xargs运行交互式应用程序,这是非常有用的
    23. -P, --max-procs=MAX_PROCS
    24. 每次运行最大进程;缺省值为 1。如果 MAX_PROCS 为 0,xargs 将一次运行尽可能多的进程。一般和 -n 或 -L 选项一起使用
    25. -p, --interactive
    26. 当每次执行一个 argument 的时候询问一次用户
    27. --process-slot-var=NAME
    28. 将指定的环境变量设置为每个正在运行的子进程中的唯一值。一旦子进程退出,将重用该值。例如,这可以用于初始负荷分配方案
    29. -r, --no-run-if-empty
    30. 当 xargs 的输入为空的时候则停止 xargs,不用再去执行后面的命令了。为默认选项
    31. -s, --max-chars=MAX_CHARS
    32. 命令行的最大字符数,指的是 xargs 后面那个命令的最大命令行字符数,包括命令、空格和换行符。每个参数单独传入 xargs 后面的命令
    33. --show-limits
    34. 显示操作系统对命令行长度的限制
    35. -t, --verbose
    36. 先打印命令到标准错误输出,然后再执行
    37. -x, --exit
    38. 配合 -s 使用,当命令行字符数大于 -s 指定的数值时,退出 xargs
    39. --help
    40. 显示帮助信息并退出
    41. --version
    42. 显示版本信息并退出

    使用带有确认消息的xargs

    创建文件配合touch命令使用时

    可以使用 -p(交互)选项来让 xargs 提示是否要进行下一步的操作。
    如果通过 xargs 将一串文件名的字符串传递给 touch 命令,touch 将创建这些文件。

    $ echo 'one two three' | xargs -p touch
    

    image.png
    终端上显示将要执行的命令,xargs 等待输入 yYnN 并按 Enter 来响应。如果只按了 Enter ,则视为 n。只有当当输入 yY 时才执行该命令。
    按下y和 Enter ,然后使用ls用来检查文件是否已经创建。

    $ ls one two three
    

    image.png

    Xargs-分割数据

    通过命令列一下指定目录下面文件,下面的命令可以把 ~/local 下面的所有一级目录的文件列清楚,非常方面逐层的查找文件

    find ./local/ -maxdepth 1 | xargs ls -l
    

    image.png

    Xargs-传递参数

    假设一个网站ID从1到100个用户,需要请求一下验证是否正确,可以通过下面的命令测试。

    echo {1..100} | xargs -p -n1 -Ii curl https://api.example.com/i
    

    其中 -I 后面的 i 是用来接收前面 1-100 的变量内容,赋值给后面的 curl [https://api.example.com/i](https://api.example.com/i)i,其中 -t 参数不是这个例子的关键参数,这个参数是为了在运行命令的时候打印出来具体的命令,确定使用的对,相当于 debug。具体输出内容如下

    $ echo {1..100} | xargs -p -n1 -Ii curl https://api.example.com/i
    curl https://ap1.example.com/1?...
    curl https://ap2.example.com/2?...
    curl https://ap3.example.com/3?...
    curl https://ap4.example.com/4?...
    curl https://ap5.example.com/5?...
    curl https://ap6.example.com/6?...
    

    Xargs-处理不支持管道的命令

    将xargs与多个命令一起使用-读取文本内容创建文件目录

    可以用-I(初始参数)选项来将 xargs 和多个命令一起使用。这一选项定义了替换字符串。在命令行中的任何出现替换字符串的位置,都会插入提供给 xargs 的值 。
    有点抽象,以一个实例来进行讲解。
    先用 tree 命令查看当前目录中的子目录。该 -d(directory)选项使 tree 命令忽略文件,只输出目录。

    $ tree -d
    

    image.png
    现在只有一个子目录 images 。
    在 dir.txt 这个文件中,有一些想要创建的目录的名称。先用 cat 查看其中的内容。

    $ cat dir.txt
    

    image.png
    把这些内容作为输入数据传给 xargs ,执行以下的命令:

    $ cat dirs.txt | xargs -I % sh -c 'echo %; mkdir %'
    

    这条命令执行了以下操作:

  • cat dir.txt :将 directrories.txt 文件的内容(所有要创建的目录名称)传给 xargs

  • xargs -I % :定义了替换字符串 %
  • sh -c:启动一个新的子shell。-c(commond)让 shell 读取命令。
  • ‘echo %; mkdir %’:每个都会被替换为 xargs 传过来的目录名称 。echo命令打印目录名称,mkdir 命令创建目录。

命令执行结果:
image.png
可以用 tree 验证已创建是否已创建了目录。

$ tree -d

image.png

配合wc命令使用xargs

可以使用 xargs 命令轻松地让 wc 命令计算多个文件中的单词数,字符数和行数

$ ls *.pid | xargs wc

执行结果如下:
image.png
命令运行结果显示了每个文件的统计信息以及总数。
这条命令执行了以下操作:

  • ls列出了所有的 .page 文件,并将该列表传给了xargs
  • xargs 将所有文件名传递给 wc
  • wc 将这些文件名作为命令行参数进行处理。

    将文件复制到多个位置

    可以使用 xargs 命令来用一个命令将文件复制到多个位置。
    首先,通过管道将两个目录的名称传给 xargs 。并且让 xargs 一次只将其中一个参数传递给正在使用的命令。
    想要调用 cp 两次,每次各使用两个目录中的一个作为命令行参数,可以通过将 xargs-n(max number)选项设置为 1 来实现。
    这里还使用了-v(verbose 详细信息)选项,让 cp 反馈正在执行的操作。

    $ echo ~/dir1/ ~/dir2/ | xargs -n 1 cp -v ./*.c
    

    将文件复制到了两个目录,一次复制一个目录。cp 反馈了详细信息,看到进行了哪些操作。
    image.png

    删除嵌套目录中的文件

    如果文件名中包含空格或者其他特殊字符(例如换行符),xargs 将无法正确解释这些文件名。可以使用 -0(空终止符)选项来解决这一问题。此时, xargs 将使用 null 字符作为文件名最终的分隔符。
    这里以 find 命令为例。find 有自己的选项来处理文件名中的空格和特殊字符,即 -print0(全名,空字符)选项。

    $ find . -name "*.png" -type f -print0 | xargs -0 rm -v -rf "{}"
    

    这一命令执行了以下操作:

  • find . -name “*.png”find 将从当前目录中搜索名称和 *.png 相匹配的对象,type -f 指定了只搜索文件。

  • -print0:名称将以空字符结尾,并且保留空格和特殊字符。
  • xargs -0xargs 也将考虑文件名以空值结尾,并且空格和特殊字符不会引起问题。
  • rm -v -rf “{}”rm 将反馈正在进行的操作(-v),递归进行操作(-r),不发送错误提示而直接删除文件(-f)。每个文件名替换 “{}”。

命令执行之后,将搜索了所有子目录,删除了其中匹配的文件。
image.png

删除嵌套目录

假设要删除一组嵌套的子目录,先用tree进行查看。

$ tree -d

image.png

$ find . -name "level_one" -type d -print0 | xargs -0 rm -v -rf "{}"

这条命令使用 find 在当前目录中递归搜索,搜索的目标是名为 level_one 的目录,然后将目录名通过xargs传递给 rm
这个命令和前面的命令之间的区别是,搜索的项目是最顶层目录的名称,而且-type d说明要查找的目录,而不是文件。
image.png
每个目录的名称都在删除时打印出来。可以用tree再查看效果:

$ tree -d

image.png
所有嵌套的子目录已删除了。

删除一种文件类型以外的所有文件

可以使用 findxargsrm 删除所有类型的文件而只保留一种想要保留的类型的文件。这需要提供想要保留的文件类型。
-not 选项让 find 返回所有与搜索模式不匹配的文件名。此时再次使用 xargs-I (初始参数)选项。这次定义的替换字符串为 {} 。这和之前使用的替换字符串 % 的效果是相同的。

$ find . -type f -not -name "*.sh" -print0 | xargs -0 -I {} rm -v {}

image.png
命令执行之后,再通过 ls 来确认结果。可以看到,目录中只剩下了与 *.sh 相匹配的文件。

$ ls -l

image.png

使用Xargs创建压缩文件

可以使用 find 命令来搜索文件,并通过 xargs 将文件名传给 tar 命令来创建压缩文件。
在当前目录中搜索 * .sh 文件。

$ find ./ -name "*.sh" -type f -print0 | xargs -0 tar -cvzf script_files.tar.gz

命令执行结果将列出了所有 .sh 文件,并创建了压缩文件。
image.png

使用Xargs配合curl访问URL

有一个文件里面有 20个 URL,编写一个命令,分别请求这些URL。文件内容如下,文件名(urls.txt)。

https://www.baidu.com
https://www.google.com
https://www.alibaba.com
https://www.qq.com
https://www.mi.com

循环来处理读取文本再执行curl进行访问
for i in `cat urls.txt`
do
curl $i
done

通过xargs处理
cat urls.txt | xargs curl

使用Xargs配合mkdir命令创建多文件目录

echo "one two three" | xargs mkdir

上面代码中,mkdir 会新建三个子目录,因为 xargsone two three 分解成三个命令行参数,执行 mkdir one two three

将 Shell 的特殊字符反引号还原为一般字符

echo '`0123`4 56789' | xargs -t echo
echo `0123`4 56789 
`0123`4 56789

如果直接进行如下操作,会报无法找到命令 01234 的错误,因为反引号在 Shell 中会将 01234 作为一个命令来执行,但是 01234 不是一个命令。-t 表示先打印命令,然后再执行。

echo `01234` 56789
-bash: 01234: command not found
56789

设置 xargs 读入参数时的结束标识,以逗号结束

这里要注意结束标志必须要是单独的字段,即以空格或者换行符分隔的字段。

echo 01234 , 56789 | xargs -E ","
01234

使用 rm、mv 等命令同时操作多个文件

使用 rm、mv 等命令同时操作多个文件时,有时会报 “argument list too long” 参数列表过长的错误,此时可以使用 xargs 来解决。xargs 将标准输入的字符串分隔后,作为参数传递给后面的命令。例如,给当前目录的所有文件添加后缀名。

ls | xargs -t -i mv {} {}.bak
# 选择符合条件的文件
ls | grep -E "201701|201702|201703" | xargs -I {} mv {} {}.bak

设置命令行的最大字符数

参数默认一个一个单独传入命令中执行。

echo "01234 56789" | xargs -t -s 11
echo 01234 
01234
echo 56789 
56789

设置标准输入中每次多少行作为命令的参数

默认是将标准输入中所有行的归并到一行一次性传给命令执行。

echo -e "01234\n56789\n01234" | xargs -t -L 2 echo
echo 01234 56789 
01234 56789
echo 01234 
01234

将文件内容以空格分隔合并为一行输出

# 列出文件内容
$ cat test.txt
a b c d e
f g h i j 
k l m n o
# 多行输入合并为一行输出
$ cat test.txt | xargs
a b c d e f g h i j k l m n o

与 ps、grep、awk 和 kill 结合,强制终止指定进程

ps -ef | grep spp | awk '{printf "%s ",$2}' | xargs kill -9

ps \-ef|grep spp用于查找包含 spp 的进程,awk '{printf "%s ",$2,FNR}将目标进程 ID 打印输出,xargs kill \-9则将目标进程 ID 作为参数传递给kill \-9用于杀死进程。