求助:编写按指定大小分类文件的Shell脚本
嘿,你的思路方向没问题,但确实用ls -l提取文件大小是个容易踩坑的做法——毕竟ls的输出是给人读的,格式可能随系统、locale甚至终端设置变化,碰到带空格、换行符或者特殊字符的文件名时,用cut之类的工具解析绝对会出问题。给你两个更可靠的实现方案:
推荐实现方案
1. 用stat命令写Shell脚本(兼容bash/zsh)
stat是专门用来获取文件元数据的工具,能精准拿到文件大小,完全不受文件名特殊字符影响。不同系统的stat参数略有区别,下面是跨平台的完整示例脚本:
#!/bin/bash # 检查输入参数是否正确 if [ $# -ne 2 ]; then echo "用法: $0 <目标目录路径> <大小阈值(字节)>" exit 1 fi TARGET_DIR="$1" THRESHOLD="$2" # 创建用于存放分类文件的子目录(-p参数确保父目录不存在时也能创建) LARGE_DIR="$TARGET_DIR/larger_than_$THRESHOLD" SMALL_DIR="$TARGET_DIR/smaller_than_$THRESHOLD" mkdir -p "$LARGE_DIR" "$SMALL_DIR" # 遍历目标目录下的所有条目,只处理普通文件 for file in "$TARGET_DIR"/*; do if [ -f "$file" ]; then # 获取文件大小:Linux用stat -c "%s",macOS用stat -f "%z" if command -v stat >/dev/null 2>&1; then if [[ "$OSTYPE" == "darwin"* ]]; then FILE_SIZE=$(stat -f "%z" "$file") else FILE_SIZE=$(stat -c "%s" "$file") fi else echo "错误:系统未安装stat命令" exit 1 fi # 比较大小并移动文件 if [ "$FILE_SIZE" -gt "$THRESHOLD" ]; then mv "$file" "$LARGE_DIR/" elif [ "$FILE_SIZE" -lt "$THRESHOLD" ]; then mv "$file" "$SMALL_DIR/" fi # 注:这里没有处理等于阈值的文件,你可以根据需求添加对应的逻辑 fi done echo "文件分类完成!大于$THRESHOLD字节的文件存放在$LARGE_DIR,小于的存放在$SMALL_DIR"
2. 用find命令快速实现(更简洁)
find自带文件大小筛选功能,一行命令就能搞定批量移动,不需要手动遍历:
#!/bin/bash if [ $# -ne 2 ]; then echo "用法: $0 <目标目录路径> <大小阈值(字节)>" exit 1 fi TARGET_DIR="$1" THRESHOLD="$2" LARGE_DIR="$TARGET_DIR/larger_than_$THRESHOLD" SMALL_DIR="$TARGET_DIR/smaller_than_$THRESHOLD" mkdir -p "$LARGE_DIR" "$SMALL_DIR" # 移动大于阈值的文件(+后面的数字加c表示字节,换成k就是KB,M就是MB) find "$TARGET_DIR" -maxdepth 1 -type f -size +"$THRESHOLD"c -exec mv {} "$LARGE_DIR" \; # 移动小于阈值的文件 find "$TARGET_DIR" -maxdepth 1 -type f -size -"$THRESHOLD"c -exec mv {} "$SMALL_DIR" \; echo "分类完成!"
这里的-maxdepth 1确保只处理目标目录下的文件,不会递归到子目录里,符合你的需求。
为什么不推荐用
ls -l? 主要有两个硬伤:
- 输出格式不稳定:不同系统、不同语言环境下,
ls -l的列位置或显示格式可能变化,虽然大部分默认第5列是文件大小,但风险始终存在。 - 特殊文件名解析失败:如果文件名包含空格、换行符或者特殊符号,
ls的输出会被拆分成多个字段,用cut -d' ' -f5提取的内容完全错误,甚至导致脚本执行异常。
最后补充个小细节:如果需要处理隐藏文件(比如.gitignore这类),在bash里可以先执行shopt -s dotglob开启显示隐藏文件的选项,这样遍历的时候就能包含它们了。
内容的提问来源于stack exchange,提问作者Arnau Martínez
相关产品推荐
相关产品推荐

