Bash实现含40万+pdb文件大目录均等拆分至子文件夹方案
最优实现方案
针对40万+量级的文件拆分,原框架直接用glob遍历的写法很容易触发shell的Argument list too long参数长度上限报错,而且循环内重复判断创建文件夹也会拖慢效率,推荐按下面的逻辑实现:
核心优化点
- 用
find命令做文件遍历,从根源避免大量文件场景下的参数长度溢出问题,同时兼容带空格、特殊字符的文件名 - 用计数器取模的方式实时计算文件归属的分组,不需要提前把所有文件路径加载到内存,内存占用恒定,适合超大量文件场景
- 提前一次性创建所有目标目录,避免循环内重复做目录存在判断
- 增加进度打印,方便跟踪几十万文件的处理进度
- 加入shell安全模式,避免变量未定义、命令执行失败导致的脏写入问题
完整可运行脚本
#!/bin/bash set -euo pipefail home="$PWD" project='ALL_pdb' input="${home}/${project}" output="${home}/${project}_parts" format='pdb' total_parts=4 # 需要拆分的总份数,可按需修改 # 提前创建输出根目录和所有子part目录 mkdir -p "$output" for i in $(seq 1 $total_parts); do mkdir -p "${output}/${project}_part_${i}" done # 统计待处理文件总数 total_files=$(find "$input" -maxdepth 1 -type f -name "*.${format}" | wc -l) echo "检测到 ${total_files} 个.${format}格式文件,准备拆分为${total_parts}份" file_count=0 # 遍历所有文件,-print0和read -d ''组合兼容任意合法文件名 find "$input" -maxdepth 1 -type f -name "*.${format}" -print0 | while IFS= read -r -d '' file_path; do # 计算当前文件归属的分组,轮询分配保证各组文件数差不超过1 part_idx=$(( (file_count % total_parts) + 1 )) target_dir="${output}/${project}_part_${part_idx}" # 复制文件,-- 兼容以-开头的文件名 cp -- "$file_path" "$target_dir/" file_count=$((file_count + 1)) # 每处理1万个文件打印一次进度 if [ $((file_count % 10000)) -eq 0 ]; then echo "已处理 ${file_count}/${total_files} 个文件" fi done # 输出最终各分组的文件数统计 echo -e "\n拆分完成,各分组文件数量:" for i in $(seq 1 $total_parts); do part_count=$(ls -1 "${output}/${project}_part_${i}" | wc -l) echo "${project}_part_${i}: ${part_count} 个文件" done
补充说明
- 上述脚本采用轮询分配规则,能保证4个分组的文件数量差最大为1,完全满足均等拆分要求,不依赖文件名的数字序号规则,就算文件命名乱序也能均匀分配
- 如果需要按文件名的数字区间拆分(比如前1/4序号的文件全放part1),可以把取模计算的逻辑替换为提取文件名中的数字,除以单组容量计算分组编号即可
- 如果需要移动文件而非复制,把脚本中的
cp替换为mv即可 - 脚本默认只处理输入目录下的一级文件,不会递归遍历子目录,如果需要递归处理去掉
find命令里的-maxdepth 1参数即可
内容的提问来源于stack exchange,提问作者James Starlight
相关产品推荐
相关产品推荐

