以前是“内事不决问baidu,外事不决问google”,现在多了一个选择,可以问AI。比如删文件这件事,当文件数达到亿级别,光输出目录下的文件名列表,日志文件大小都可以达到G级别。当数据量大了之后,执行串行 rm 操作将花费大量的时间删文件,只有通过增加并发才能减少所花费的时间。
通过 Python 写多进程或多线程脚本,控制 rm 删除操作的并发数这可很好地解决这个问题。但这样会额外引入不必要的复杂度,如果能用一条命令就解决就更好了。毕竟删文件,方法越简单越好。
问 AI 在 bash 脚本中如何控制并发?AI 给出了四种方案,都很有道理。
方案一:使用 & 后台运行
在Bash脚本中使用&符号将任务置于后台运行,从而实现并发执行。
例如:
# 启动任务1后台运行
task1 &
# 启动任务2后台运行
task2 &
# 等待任务1和任务2完成
wait
方案二:使用 sem 命令
使用sem命令,它允许您指定并发任务的数量。首先,您需要安装sem工具,然后可以在Bash脚本中使用它。
以下是一个示例:
# 安装 sem 工具
sudo apt-get install parallel
# 使用 sem 控制并发任务
max_jobs=4
sem -j $max_jobs your_command_1
sem -j $max_jobs your_command_2
sem -j $max_jobs your_command_3
sem --wait
方案三:使用 xargs 命令
使用xargs命令来控制并发任务。
例如:
# 使用 xargs 控制并发任务
max_jobs=4
cat command_list | xargs -P $max_jobs -I {} bash -c '{}'
这将使用xargs命令并行执行命令列表,最多允许4个并发任务。
方案四:使用 GNU Parallel:
GNU Parallel是一个强大的工具,可以用于并行执行任务。可以创建一个文本文件 commands.txt 包含一系列命令,使用 -j 选项,您可以指定允许同时运行的任务数。
例如,要限制最多同时执行 4 个任务:
parallel -a commands.txt -j 4
方案一无法控制大量任务,方案三和四要额外安装软件。我选择方案三,并让 AI 解释方案二中的命令都是什么意思。
# 使用 xargs 控制并发任务
max_jobs=4
cat command_list | xargs -P $max_jobs -I {} bash -c '{}'
以上命令解释如下:
-
-P 选项后面跟着一个数字代表您想要的最大并发任务数。
-
-I {} 这部分告诉xargs如何将输入的数据插入到命令中‘{}’是一个占位符,xargs会将每个命令依次插入到{}的位置。
-
bash -c '{}': 这部分是要执行的命令,其中{}将被xargs替换为command_list中的命令。
可以构造一个随机耗时脚本来测试并发,验证通过后来再用来删文件。使用随机数模拟并发中的程序耗时,脚本如下:
# /tmp/sleep.sh
min=1
max=100
random_number=$((min + RANDOM % (max - min + 1)))
echo sleep $random_number
sleep $random_number
再构造一下命令列表:
for i in $(seq 1 100); do
echo "bash /tmp/sleep.sh" /tmp/command_list
done
最后使用 xargs 调用命令列表:
$ max_jobs=4
$ cat /tmp/command_list | xargs -P $max_jobs -I {} bash -c '{}'
sleep 87
sleep 28
sleep 44
sleep 40
通过执行测试命令可以看出每次执行四条命令,当有命令提前执行完成时,xargs 会将任务数补充至所要求的最大并发数 max_jobs
在测试命令中所使用的 -I {} 是占位符,我们可以使用其它占位符替代,比如命令可以写成:
$ cat /tmp/command_list | xargs -P $max_jobs -I @@ bash -c '@@'
我们也可以将 bash -c 换成 echo 命令,测试我们将要执行的命令。echo 将会将占位符的内容打印出来,方便我们核对命令是否正确。删除操作是非常危险的,执行前一定要再三确认。
下面正式开始我们的删除任务,所要删除的文件都在一级目录,我们将要删除的一级目录通过 find 命令构造一个删除任务列表:
find /sync/trans/192.168.10.23/pacs/172.16.100.68
-mindepth 2
-maxdepth 2
-type d
| awk '{print "rm -rf "$1}'
| tee /tmp/command_list
最终任务列表如下:
cat /tmp/command_list
rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201217_20
rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201123_20
rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201212_20
rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201214_20
rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201009_20
因为任务会长时间运行,在正式执行前先启动一个 screen 会话,用来维持运行的环境。
$ screen -S rm_task
在 screen 会话中执行删除任务:
cat /tmp/command_list | xargs -P 8 -I @@ echo '@@'
在执行前用 echo 命令测试了一个命令是否正确,然后将 echo 换成 bash -c 正式执行:
cat /tmp/command_list | xargs -P 8 -I @@ bash -c '@@'
实际执行时并没有命令输出,因为 rm 操作没有输出。之前的 sleep 命令有输出是因为通过脚本包装了一层,执行前输出了要执行的命令。如果要查看当前命令是否正在执行,可以通过 ps 命令查看:
ps -eaf| grep rm
pts/1 00:00:03 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201202_20
pts/1 00:00:05 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20210119_20
pts/1 00:00:03 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201129_20
pts/1 00:00:03 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201218_20
pts/1 00:00:03 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201209_20
pts/1 00:00:03 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201213_20
pts/1 00:00:03 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20201127_20
pts/1 00:00:03 rm -rf /sync/trans/192.168.10.23/pacs/172.16.100.68/images/20200907_20
通过 ps 命令可以看到并发的 rm 操作有 8 个,这些任务都是 xargs 的子进程:
$ pstree -p
screen(15040)───bash(15043)───xargs(1743)─┬─rm(1744)
├─rm(1745)
├─rm(1746)
├─rm(1747)
├─rm(1748)
├─rm(1749)
├─rm(1750)
└─rm(1751)
注意
删除操作是一个风险极高的操作,在使用你不了解的技术前一定要多多验证,一个空格可能就会造成删盘的惨案。
以上技巧除了可用来构造大量小文件的删除操作外,也可用于构造小文件的拷贝、验证任务。低成本的并发控制可以少写不少代码,之前用python构造的小文件并发拷贝任务的文章可以作为参考:
全文完。
如果转发本文,文末务必注明:“转自微信公众号:生有可恋”。
原文始发于微信公众号(生有可恋):Linux 删除大量小文件 -- AI方案
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论