xargs 命令是可以将标准输入改变输出格式作为参数传递给其他命令,弥补有些命令(如 echorm 等)不接受管道传参。

标准输入

默认 xargs 把多行转为一行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
(base) root@ubuntu-studio:/workspace# find jinzhongxu/codes/ -name "*.py" -type f -print
jinzhongxu/codes/py/test-debug/main.py
jinzhongxu/codes/py/audio/server.py
jinzhongxu/codes/py/audio/client.py
jinzhongxu/codes/py/audio/record.py
jinzhongxu/codes/py/pys/mymodule/utils/unzip.py
jinzhongxu/codes/py/pys/mymodule/utils/TimeStamps.py
jinzhongxu/codes/py/pys/mymodule/utils/ipynb.py
jinzhongxu/codes/py/pys/mymodule/cv/gif.py
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py
jinzhongxu/codes/py/pys/mymodule/cv/bbox_video.py
jinzhongxu/codes/py/pys/mymodule/cv/video.py
jinzhongxu/codes/py/pys/mymodule/cv/image.py
jinzhongxu/codes/py/pys/mymodule/cv/video_cut.py

# 把 find 的输出结果转为一行。当 xargs 后增加参数 -n num 可以输出多行,如 -n 2 输出 2 行
(base) root@ubuntu-studio:/workspace# find jinzhongxu/codes/ -name "*.py" -type f -print | xargs
jinzhongxu/codes/py/test-debug/main.py jinzhongxu/codes/py/audio/server.py jinzhongxu/codes/py/audio/client.py jinzhongxu/codes/py/audio/record.py jinzhongxu/codes/py/pys/mymodule/utils/unzip.py jinzhongxu/codes/py/pys/mymodule/utils/TimeStamps.py jinzhongxu/codes/py/pys/mymodule/utils/ipynb.py jinzhongxu/codes/py/pys/mymodule/cv/gif.py jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py jinzhongxu/codes/py/pys/mymodule/cv/bbox_video.py jinzhongxu/codes/py/pys/mymodule/cv/video.py jinzhongxu/codes/py/pys/mymodule/cv/image.py jinzhongxu/codes/py/pys/mymodule/cv/video_cut.py

# grep 接收一行文件名,但不能接收多行文件名
(base) root@ubuntu-studio:/workspace# find jinzhongxu/codes/ -name "*.py" -type f -print | xargs grep -n sam --color=always
jinzhongxu/codes/py/audio/server.py:30: format=p.get_format_from_width(wf.getsampwidth()),
jinzhongxu/codes/py/audio/record.py:6:chunk = 1024 # Record in chunks of 1024 samples
jinzhongxu/codes/py/audio/record.py:7:sample_format = pyaudio.paInt16 # 16 bits per sample
jinzhongxu/codes/py/audio/record.py:9:fs = 44100 # Record at 44100 samples per second
jinzhongxu/codes/py/audio/record.py:21: format=sample_format,
jinzhongxu/codes/py/audio/record.py:46: wf.setsampwidth(p.get_sample_size(sample_format))
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:12:from segment_anything import SamPredictor, sam_model_registry
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:36:def sam_image_annotation(
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:43: sam_checkpoint="/disk1/datasets/models/sam/sam_vit_h_4b8939.pth",
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:110: sam = sam_model_registry[model_type](checkpoint=sam_checkpoint)
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:111: sam.to(device=device)
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:112: predictor = SamPredictor(sam)
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:353: default="/disk1/datasets/models/sam/sam_vit_h_4b8939.pth",
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:378: sam_image_annotation(
jinzhongxu/codes/py/pys/mymodule/cv/image_sam_annotate.py:385: sam_checkpoint=checkpoint,

在 Linux 系统中我们常使用管道符 | 将上一个命令的标准输出转化为标准输入提供给管道后面的命令,如:

1
echo "hello\nworld"  | grep e

结果

1
hello

命令 grep 能够将管道符的标准输入作为参数进行处理,但并不是所有命令都支持,如 rm 命令:

1
ls /home/jinzhongxu/hello

结果

1
1.txt 2.txt 3.txt

如果我想删除文件夹 hello 下面的所有文件,我尝试如下命令:

1
ls | rm

结果

1
2
rm: missing operand
Try 'rm --help' for more information.

此时,可以采用 xargs 命令来解决

1
ls | xargs rm

xargs 命令

xargs(eXtended ARGuments)是给命令传递参数的一个过滤器,它的作用就是将标准输入转为命令行参数。默认情况下 xargs 等同于 xargs echo。可以为 xargs 指定不同的参数达到不同的功能,如 -n 表示按几行显示,参数后面跟上整数,表示行数。

分隔符

默认 xargs 把空格作为分隔符,但可以使用参数 -d 指定分隔符,如:

1
2
# 以空格为分隔符,每行显示 1 个
echo 'a b c' | xargs -n1

结果

1
2
3
a
b
c

但是,如果符号之间的分隔符不是空格,而是其他时,就会失效

1
echo 'a.b.c' | xargs -n1

结果

1
a.b.c

如果指定分隔符 .,就可以:

1
echo 'a.b.c' | xargs -d. -n1

结果

1
2
3
a
b
c

参数替换

如果管道符后面的命令获取的参数不是放在最后以为,需要使用参数替换,在 xargs 中用参数 -I 来指定替换字符,如:

1
2
把当前路径软链接到 /home/jinzhongxu/hello 下的 new-link
echo $(pwd) | xargs -I {} ln -s {} /home/jinzhongxu/hello/new-link

这里替换符 {} 也可以换成其他的符号,如:

1
2
3
4
# 用 currentDir 作为替换符
# 注意替换符不要出现在命令其他参数中
echo $(pwd) | xargs -I currentDir ln -s currentDir /home/jinzhongxu/hello/new-link
echo `pwd` | xargs -I currentDir ln -s currentDir /home/jinzhongxu/hello/new-link

另一个例子,从文件中读取字符,创建同名的文件夹:

1
cat filename.txt | xargs -I file sh -c 'mkdir file'

执行提示

使用参数 -p 可以提示我们执行的命令,并让我再次判断是否执行:

1
2
3
4
# 创建多个文件
echo '1\n2\n3' | xargs -i touch {}.txt
# 删除多个文件
ls | xargs -p rm

使用参数 -t 会在终端显示出执行的命令,先打印命令,然后再执行:

1
2
3
4
# 创建多个文件
echo '1\n2\n3' | xargs -i touch {}.txt
# 删除多个文件
ls | xargs -t rm

文件名中有空格

有些文件名中带有空格,然而 xargs 的默认分隔符就是空格,这导致无法处理带有空格的文件。此时,可以使用参数 -0 指定 null 作为分隔符,同时结合 find 命令的参数 print0 把文件列表以 null 分割:

1
2
3
4
# 创建多个文件
echo '1\n2\n3' | xargs -i touch {}.txt
# 按照文件名删除文件
find . -name "*txt" -print0 | xargs -0 rm

上面命令删除当前路径下的 txt 文件。由于分隔符是null,所以处理包含空格的文件名,也不会报错。有些命令(比如 rm)一旦参数过多会报错”参数列表过长”,而无法执行,改用 xargs 就没有这个问题,因为它对每个参数执行一次命令。

多行参数

使用参数 -L 可以指定多少行作为一个命令行参数。

1
2
3
xargs find -name
"*.docx"
"*.textile"

上面命令会报错,可以尝试使用如下命令:

1
2
3
xargs -L 1 find -name
"*.docx"
"*.textile"

多进程

xargs 默认只用一个进程来执行命令。使用 --max-procs 参数可以指定同时用几个进程来并行执行。 如 --max-procs 3 表示同时使用 3 个进程,--max-procs 0 表示 as many as it can,使用所有 CPU 核心执行。如下命令表示同时关闭尽可能多的 Docker 容器

1
docker ps -q | xargs -n 1 --max-procs 0 docker kill

参考文献

  1. xargs 命令教程
  2. Linux xargs 命令
  3. xargs命令