find,xargs,tar 如何命令一起使用

目录

   有一个需要是,把一个目录下面前一天的日志文件用 tar 进行打包备份,直觉的想到要用 find,xargs,tar 命令来做,但是遇到一些问题,
总的来说还是对这几个命令中的一些细节没有搞清楚,这里终结一下。

xargs

简单的说 xargs 的作用是可以把标准输入的东西做为命令的参数来使用,比如要过滤一天前的文件,我们可以这样写

# 这里就把 find 找到的文件做为了 grep 的参数来使用
find . -mtime +1 |xargs -i grep sometine {}

-i 和不加-i 的区别

-i 可以给标准输入的东西起一个别名,默认是{},这个别名可以灵活的插入命令中的任何地方。
 但在这里有一个我以前一直忽略的细节,就是加了-i 之后,传给后面命令的参数总是一行一行的,就算加了-n 选项也没有用。
不加-i 默认是把所有的标准输入作为一行,传到命令的参数中,除非加-n 指定传入的行数,这个细节非常重要。下面验证一下。

先创建一个脚本 f.sh 这个脚本就是简单的输出传给它的参数个数,我们用这个脚本来验证 xargs 的机制

#!/bin/bash

echo $#

下面我们用我们的脚本验证一下 xargs 的机制

# 这里可以看到把所有的标准输出作为一行全部作为 f.sh 的参数
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ seq 100   |xargs   f.sh 
100
# 加了-i 后我们可以看见是一行一行的处理
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ seq 100   |xargs -i  f.sh {}
1
1
1
1
1
1
1
1
1
......
# 这里可以看到-n 50 就是把标准输入每 50 个一组的给 f.sh 脚本作为参数
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ seq 100   |xargs -n 50  f.sh 
50
50
# 这里可以看到 -n 和-i 一起用的时候-n 没有生效
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ seq 100   |xargs   -n 50 -i  f.sh {}
1
1
1
1
1
1
1
1
1
1
1
1
......

-P 参数

-P N 可以让我们开启 N 个进程并行的处理标准输入过来的东西,下面验证一下

不开启多进程处理

jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ time seq 100000   |xargs   -n 50   f.sh
......
50
50
50
50
50
50
50

real  0m2.848s
user  0m0.060s
sys 0m0.256s

开启两个进程处理,可以看到快了差不多一倍

jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ time seq 100000   |xargs   -n 50 -P2  f.sh 
......
50
50
50
50
50
50

real  0m0.875s
user  0m0.036s
sys 0m0.128s

-0 参数

     -0 和–null 的作用是一样的,这个一般我们可能需要和 find 的-print0 结合起来使用。在一般情况下命令参数的分割符号是空白,
也就是说 我们把 ls a b 中的 a 和 b 作为两个参数传入给 ls,但是万一坑爹的有个文件叫“a b”怎么办呢。这个有时非常危险,考虑下面
这个命令: find /Logs -mtime +1 type |xargs -i rm -rf {} ,我们想的是删除/Logs 中的文件,看起来没毛病,但是如果有个文件叫“.. test.log”
这样就会错误的删除其他的文件,所以我们需要在 find 中加 -prinit0 这个的作用就是在文件名的后面加一个 NUL,然后在 xargs 中加一个-0
这个的作用就是让 xargs 把 NUL 作为个参数的分割符号。下面看验证代码。

jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ touch "t t.log"
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ ls
t t.log
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ touch ".. f"
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ ls -a
.  ..  .. f  t t.log
# 看见没有这个太可怕了,万一我不是 ls 而是 rm 就把重要文件删除了
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ find . |xargs ls
ls: 无法访问'f': 没有那个文件或目录
ls: 无法访问'./t': 没有那个文件或目录
ls: 无法访问't.log': 没有那个文件或目录
.:
t t.log

./..:
1.txt data    

# 这样就对了
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ find . -print0 |xargs -0  ls
./.. f  ./t t.log

.:
t t.log

# 我们用 hd 看一下区别
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ find . |hd
00000000  2e 0a 2e 2f 74 20 74 2e  6c 6f 67 0a 2e 2f 74 2e  |.../t t.log../t.|
00000010  6c 6f 67 0a                                       |log.|
00000014
# 可以看到加了-print0 后文件结束位置多了个 00 这个就是 NUL,然后 xargs 就可以用这个来识别一个参数的结束
jimila@CDYJY-JINGML:~/Desktop/prolab/testshell/xargs$ find . -print0 |hd
00000000  2e 00 2e 2f 74 20 74 2e  6c 6f 67 00 2e 2f 74 2e  |.../t t.log../t.|
00000010  6c 6f 67 00                                       |log.|
00000014

tar

-T 和 –null

我们可能需要把 find 找到的文件备份,比如文章一开始说道的我们的任务,可以像下面这样写

# 日志是有格式的所以可以按格式过滤出前一天的文件
# --null 和 xargs 中的-0 作用是一样的, -T 是指定从哪里读取要压缩的文件名 最后一个 - 代表标准输入
d=`date +"%Y-%m-%d_%H-%M-%S"`;ls /Log/* |grep `date -d'-1 day' +'%Y-%m-%d'`| xargs -i find {} -type f -print0 |tar czvf "$d.tar.gz" --null -T -

目录