管道符_+xargs拓展参数传递操作,以及find指令
一个简单的例子:
我想删除tex2,但是保留tex1以及其他文件(更一般的,我想删除某种pattern模式下某个子集文件,但是保留其他在该匹配模式下的文件)
ls -lh | grep -v tex1 | rm tex
但是真实删除的却是所有的tex*文件,我本意是想删除tex2的,所以是为什么呢 ?
道理很简单:
管道符 |
的作用是将前一个命令的输出传递给后一个命令的标准输入。然而,rm tex*
并不会读取标准输入,而是直接根据当前目录下的文件匹配模式 tex*
删除文件。
说白了就是rm正常实现下是无法接受上一个命令的标准输出的,也就是rm不会读取标准输入。
因此,rm tex*
忽略了 grep
的输出,直接删除了所有以 tex
开头的文件。
事实上,大多数命令都不接受标准输入作为参数,只能直接在命令行输入参数,这导致无法用管道命令传递参数。
正确的实现方法如下:
1,使用 xargs
也就是拓展管道命令符的参数传递command,
能够捕获一个命令的输出,然后传递给另外一个命令,正好弥补了一些命令无法单纯使用管道符传递参数的缺点;
编辑
命令格式:
command1 | xargs -para command2# 注意xargs是放在前面1个命令的后面,然后xargs有自己的参数(para等),再紧跟着自己的命令,
# 即用于沟通前面1个command1,以及后面1个command2
一些参数:
编辑
编辑
举例如下:
使用vim定义一个测试文件,内有多行文本数据:
如果只是单纯输出的话,cat或者more都只能多行输出
编辑
如果使用xargs传递参数的话,相当于和xargs的默认命令echo联用,可以整个单行输出;
即xargs 默认的命令是 echo,这意味着通过管道传递给 xargs 的输入将会包含换行和空白,不过通过 xargs 的处理,换行和空白将被空格取代。
说白了就是将前面的标准输出都当作是参数过滤过来了,然后将所有的参数都echo出来。
多行输入单行输出:
编辑
可以看到,本质上和echo联用的命令是一致的,但是单纯使用echo的话不行。
我们可以查看一下这个文件的一些转义字符之类的特殊字符的分布:
参考我的博客:如何查看某个文件中的特殊符号-CSDN博客
编辑
我们可以调整一下一次传递的参数个数,实际上就是一次打印echo出来的标准输出,
编辑
-n 选项多行输出:比如说每行传递3个参数,也就是每一行只打印其中的3个标准输出的内容
编辑
-d 选项可以自定义一个定界符:
比如说以字符"X"分割,本质上和awk -F,或者cut -f中的某些特殊分隔符操作类似;
编辑
结合 -n 选项使用:
编辑
使用xargs过滤所有的参数,以X字符为分隔符,然后每行打印2个参数;
xargs一个比较特殊或者说常用的用法:
- xargs -I {}:-I 选项指定一个替换字符串 {},xargs 会将输入的每一行替换为 {},然后将其传递给后面的命令。
可以理解里面是有一个for循环,xargs 的行为类似于一个隐式的 for 循环,它会逐个处理输入的参数,并将它们传递给指定的命令。虽然 xargs 并不是真正的循环语句,但它的功能与 for 循环非常相似,尤其是在处理多个输入项时。
基本原理
xargs
从标准输入(stdin)读取数据。-I {}
指定一个占位符(通常是{}
),xargs
会将输入的每一行替换为这个占位符。- 替换后的结果会被传递给后续的命令。
xargs -I 指定占位符参数
示例1:对文件中的每一行执行命令
假设有一个文件 names.txt
,内容如下:
Alice
Bob
Charlie
目标:为每个人创建一个目录
使用 xargs -I {}
可以实现:
cat names.txt | xargs -I {} mkdir {}
cat names.txt
:将文件内容输出到标准输出。xargs -I {}
:指定占位符为{}
。mkdir {}
:xargs
会将每一行替换为{}
,然后执行mkdir
命令。
执行结果:
- 创建了三个目录:
Alice
、Bob
和Charlie
。
不一定指定占位符为{},也可以是x
示例2:对文件中的每一行执行多个参数的命令
假设有一个文件 commands.txt
,内容如下:
echo Hello
echo World
echo Linux
目标:执行文件中的每条命令
使用 xargs -I {}
可以实现:
cat commands.txt | xargs -I {} sh -c {}
cat commands.txt
:将文件内容输出到标准输出。xargs -I {}
:指定占位符为{}
。sh -c {}
:xargs
会将每一行替换为{}
,然后通过sh -c
执行命令。
执行结果:
Hello
World
Linux
示例3:处理文件列表
假设有一个文件 files.txt
,内容如下:
file1.txt
file2.txt
file3.txt
目标:将每个文件的内容追加到一个新的文件 combined.txt
使用 xargs -I {}
可以实现:
cat files.txt | xargs -I {} cat {} >> combined.txt
cat files.txt
:将文件内容输出到标准输出。xargs -I {}
:指定占位符为{}
。cat {} >> combined.txt
:xargs
会将每一行替换为{}
,然后执行cat
命令,将文件内容追加到combined.txt
。
执行结果:
combined.txt
包含了file1.txt
、file2.txt
和file3.txt
的所有内容。
示例4:批量处理图片文件
假设当前目录下有多个图片文件:
image1.jpg
image2.jpg
image3.jpg
目标:将所有图片文件复制到 /data/images
目录下
使用 xargs -I {}
可以实现:
ls *.jpg | xargs -I {} cp {} /data/images
ls *.jpg
:列出当前目录下所有.jpg
文件。xargs -I {}
:指定占位符为{}
。cp {} /data/images
:xargs
会将每个文件名替换为{}
,然后执行cp
命令,将文件复制到/data/images
。
执行结果:
/data/images
目录下包含了image1.jpg
、image2.jpg
和image3.jpg
。
示例5:批量重命名文件
假设当前目录下有多个文件:
file1.txt
file2.txt
file3.txt
目标:将所有文件重命名为 new_file1.txt
、new_file2.txt
、new_file3.txt
使用 xargs -I {}
可以实现:
ls *.txt | xargs -I {} mv {} new_{}
ls *.txt
:列出当前目录下所有.txt
文件。xargs -I {}
:指定占位符为{}
。mv {} new_{}
:xargs
会将每个文件名替换为{}
,然后执行mv
命令,将文件重命名为new_file1.txt
等。
执行结果:
- 文件被重命名为
new_file1.txt
、new_file2.txt
和new_file3.txt
。
总结
xargs -I {}
是一个非常灵活的工具,可以将输入的每一行数据替换为占位符 {}
,并将其传递给后续的命令。
现在回到我们的问题,比如说我新建了4个text文件,然后我只想保留text1,但是其余的text*文件我都想要删除(比如说这里text+text2-4都想删除,或者指定2-4删除),或者更复杂一点的命令:
ls -lh | grep -v tex1 | awk '{print $NF}'
$NF:表示当前行的最后一个字段(NF 是 awk 中的内置变量,表示字段数)
因为ls -lh实际上print在屏幕上的输出也可以看作是一个标准输出(一个文本文件),所以文件名实际上是可以使用awk获取的,直接打印最后一列,$NF(number of filed,也就是列数);
比如说:
ls -lh | grep -v text1 | ls -lh text* | awk '{print $NF}'
ls -lh | grep -v text1 | ls -lh text* | awk '{print $NF}' | xargs -I {} rm {}
当然也可以
ls -lh | grep -v tex1 | awk '{print $NF}' | xargs rm
xargs -I {}
并不局限于只能读取每行标准输出的参数,也不一定每次只能读取一行。它的行为可以根据输入数据的格式以及 xargs
的其他选项进行调整。以下是一些更详细的解释和扩展说明:
xargs
的输入来源
xargs
的输入可以来自多种来源,包括:
- 标准输入(stdin):通过管道(
|
)或重定向(<
)传递给xargs
。- 例如:
cat arg.txt | xargs -I {} echo {}
或者:
xargs -I {} echo {} < arg.txt
- 文件内容:直接从文件中读取内容。
- 使用
-a
选项:
- 使用
xargs -a arg.txt -I {} echo {}
输入数据的格式
xargs
默认会将输入数据视为由空格、制表符或换行符分隔的多个参数。不过,可以通过以下方式调整输入数据的处理方式:
- 按行处理:
- 默认情况下,
xargs
会将输入数据按行处理,每行视为一个参数。 - 如果输入数据是多行的,
xargs
会逐行处理。 - 例如:
- 默认情况下,
cat arg.txt | xargs -I {} echo {}
如果 arg.txt
的内容是:
aaa
bbb
ccc
输出结果为:
aaa
bbb
ccc
- 按空格或制表符分隔:
- 如果输入数据是用空格或制表符分隔的,
xargs
会将它们视为多个参数。 - 例如:
- 如果输入数据是用空格或制表符分隔的,
echo "aaa bbb ccc" | xargs -I {} echo {}
输出结果为:
aaa bbb ccc
- 自定义分隔符:
- 使用
-d
选项可以指定输入数据的分隔符。 - 例如,如果输入数据用逗号分隔:
- 使用
echo "aaa,bbb,ccc" | xargs -d, -I {} echo {}
输出结果为:
aaa
bbb
ccc
每次处理的参数数量
xargs
的行为可以通过以下选项进行调整:
-n
** 选项**:- 指定每次传递给命令的参数数量。
- 例如:
echo "aaa bbb ccc" | xargs -n 2 -I {} echo {}
输出结果为:
aaa bbb
ccc
这里,-n 2
表示每次传递两个参数给 echo
命令。
-I
** 选项**:- 指定一个替换字符串
{}
,xargs
会将输入的每个项目替换为{}
,并传递给命令。 - 如果不使用
-n
,xargs
默认会将每个输入项目单独传递给命令。 - 例如:
- 指定一个替换字符串
echo "aaa bbb ccc" | xargs -I {} echo {}
输出结果为:
aaa
bbb
ccc
-0
** 选项**:- 如果输入数据是以 null(
\0
)字符分隔的,可以使用-0
选项。 - 这在处理包含空格、换行符等特殊字符的文件名时非常有用。
- 例如:
- 如果输入数据是以 null(
find . -type f -print0 | xargs -0 -I {} echo {}
这里,find
命令的 -print0
选项会以 null 字符分隔文件名,xargs -0
会正确处理这些文件名。
总结
xargs -I {}
不仅可以处理每行输入,还可以根据输入数据的格式和xargs
的其他选项进行灵活调整。- 通过
-n
可以控制每次传递的参数数量,通过-d
可以指定分隔符,通过-0
可以处理以 null 分隔的输入。 xargs
是一个功能强大的工具,可以根据具体需求灵活使用。
参考:Linux xargs 命令 | 菜鸟教程
2,使用find命令:
除了使用xargs,还可以使用find命令,
参考:https://www.runoob.com/linux/linux-comm-find.html
find参数传递的命令格式也很简单,就是
-exec 执行动作的命令 {} \
其中{}也是一个占位符,指代前面的文件;
比如说:
find . -name "tex*" ! -name "tex1" -exec rm {} \;
具体实施就是:
我只想保留te1,其余的te*都删除
这里其实涉及到find命令的组合命令了,也就是条件组合表达形式:
其中逻辑运算符中and是默认的,所以一般命令看起来是连写的:
比如说:
find . -name "te*" -and -size 0 -exec echo "deleting:" {} \; -exec rm {} \;
在当前目录下寻找文件名匹配/通配te*的文件,且文件尺寸为0的文件,在提醒删除的同时,将该文件删除
回归问题:
find . -name "te*" ! -name "te1" -exec rm {} \;
总之就是:
在当前目录下寻找文件名匹配/通配te*的文件,但是排除te1文件,然后将每一个文件名都传递给{}占位符,然后对这些占位符执行rm删除命令
至于exec,其实是可以同时执行多项组合操作:
find 可以通过多个 -exec 或其他操作选项同时执行多种操作。例如:
示例 1:同时打印文件名和删除文件
find . -name "*.log" -exec echo "Deleting:" {} \; -exec rm {} \;
示例 2:查找并移动文件
find . -name "*.txt" -exec mv {} /path/to/destination/ \;
示例 3:查找并压缩文件
find . -name "*.log" -exec gzip {} \;
xargs可以和find联用:
比如说上面倒数第2个的命令,使用-print其实就是将找到文件路径打印到标准输出(通常是终端),然后再经过xargs过滤参数传递给tar命令