S3 对象批量重命名快捷方法
本文所述操作适用于兼容 S3 协议的所有存储框架,包括 AWS S3
、Aliyun OSS
、MinIO
、Ceph
等。
不知为何,截止目前,S3 协议并不包含对象重命名的接口。如果有重命名对象的需求,一般能想到的就是重新上传改名之后的对象,然后从存储桶中将原名对象删除。很明显,这种方式好比大炮打蚊子,目的达到了,累得一身汗,要是本地没有备份,还得先下载,费时费力费钱。特别是当待处理的对象的数量相当庞大的时候,如果不想持续加班一个月,那就要琢磨琢磨另辟蹊径才行。
能不能将存储桶挂载到本地,然后用本地 shell 命令操作其中的对象呢?值得一试!
挂载
使用 rclone
、s3fs-fuse
、goofys
等工具挂载,这里以 goofys 为例。
- 直接下载编译好的执行文件
wget https://github.com/kahing/goofys/releases/latest/download/goofys
- 设置执行权限
chmod +x goofys
- S3 密钥配置
mkdir ~/.aws
vi ~/.aws/credentials
# 以下是 ~/.aws/credentials 内容
[default]
aws_access_key_id = xxxxxxx
aws_secret_access_key = yyyyyyyy
- 创建挂载点
mkdir /mnt/foo
- 挂载
mkdir /opt/goofys
./goofys --endpoint https://us-east.s3.aws.com bucketName /mnt/foo/
- 验证是否挂载成功。
# 列出前 10 个文件
ls /mnt/foo/ | head -n 10
# 注意:该语句并不能减少 ls 的执行时间。当文件数量过多时,可以使用通配符减少 ls 的罗列数量。
重命名
挂载成功之后,我们就可以采用 mv
或 rename
指令尝试重命名文件了,下面以 perl 版本的 rename 为例(该版本支持正则表达式)。
# 所有文件名只保留后 5 个字符,并更改扩展名
rename 's/\w*?(\w{5})\.mdi/$1.obj/' *
# 实际也是逐个文件处理,而非一次性同时处理,文件多则耗时
# 如果文件太多则会报 "Argument list too long"(虽然参数只有一个星号,但实则是将所有文件名查找出来后执行),可以改成如下方式:
find . -name "*" | xargs rename 's/\w*?(\w{5})\.mdi/$1.obj/'
# 虽然不会报错,但其实只是将查找文件名环节转给了 find,总耗时是一样的
为了更好更灵活地重命名巨量文件,只能编写脚本了。以下是示例代码:
rename_s3_objects()
{
local renamedCnt=0
for obj in ./*
do
if [ `expr ${#obj} - 2` -gt 8 ]; then # 如果不作判断,那么不管文件名是否符合,正则都会执行(耗时),然后再根据执行结果看是否重命名(重命名比正则更耗时)
rename 's/\w*?(\w{5})\.mdi/$1.obj/' $obj
fi
renamedCnt=$(($renamedCnt + 1))
if [ `expr $renamedCnt % 100` -eq 0 ]; then
# 每处理完 100 个对象就输出提示
echo "$renamedCnt objects renamed"
fi
done
return 0
}
rename_s3_objects
# 似乎 ``; $[]; $(()) 都可以执行运算
保存后,在命令行中使用 sh saved_file_name
执行即可。
经测算,在普通网络环境下,每重命名 100 个对象(大约 100M),耗时 70s 左右。并未检查过程中是否涉及到对象的传输(博主试验的服务器并没有带宽使用统计),推测应该是没有,有兴趣的朋友可以验证下。
除重命名外,类似于[批量]删除、S3 中对象加斜杠前缀(创建文件夹并移动文件)等指令应该也可以通过挂载方式玩转。