Shell脚本中如何构建可传递给sort -n的有效列表?
问题分析与解决方案
你的脚本核心问题出在列表构建的方式和细节处理上,导致最后输出没法正确换行排序,另外还有扩展名提取效率的问题,我来一步步帮你解决:
1. 为什么你的列表无法正确排序?
你用list+=直接拼接字符串的方式,会把所有内容连在一起;而且最后echo $list时,Shell会把所有换行和空格都转换成单个空格,导致所有内容变成一行,sort -n自然没法按每行的数值排序。
另外还有几个细节漏洞:
- 路径没加双引号包裹,输入含空格的路径时脚本会出错;
- 扩展名提取逻辑有问题,比如隐藏文件
.bashrc会被错误提取成bashrc,无扩展名的文件会被当成“扩展名”是文件名本身; - 多次调用
find,文件多的时候效率很低。
2. 修复你的原有脚本
如果想保留原本的循环逻辑,我们可以调整列表的存储方式(用数组),同时修复细节问题:
#!/bin/sh echo -n "Enter directory/path to analyze: " read -r path # 用-r避免转义特殊字符 # 正确提取扩展名,处理无扩展名和隐藏文件的情况 extList=$(find "$path" -type f -exec basename {} \; | awk -F. ' { if (NF == 1) { # 无扩展名的普通文件 print "(no extension)" } else if ($0 ~ /^\./ && NF == 2) { # 隐藏文件且只有一个点(比如.bashrc),算无扩展名 print "(no extension)" } else { # 取最后一个后缀 print $NF } }' | sort | uniq) # 用数组存储结果,保留每行的结构 declare -a list for ext in $extList; do sum=0 if [ "$ext" = "(no extension)" ]; then # 查找无扩展名的文件:普通文件无点,或隐藏文件只有一个点 byteList=$(find "$path" -type f -exec basename {} \; | awk -F. ' { if (NF == 1 || ($0 ~ /^\./ && NF == 2)) print $0 }' | xargs -I {} find "$path" -type f -name "{}" -printf '%s\n') else # 查找对应扩展名的文件 byteList=$(find "$path" -type f -name "*.$ext" -printf '%s\n') fi # 计算总大小 for b in $byteList; do sum=$((sum + b)) done sum=$((sum / 1024)) # 转换成KB # 将结果添加到数组中 list+=("$sum KB $ext") done # 输出数组内容(每行一个元素)并排序 printf "%s\n" "${list[@]}" | sort -n
3. 更高效的替代方案(推荐)
上面的脚本还是多次调用find,效率不高。我们可以用一次find配合awk完成所有统计,逻辑更简洁,速度更快:
#!/bin/sh echo -n "Enter directory/path to analyze: " read -r path # 一次遍历所有文件,用awk统计每个扩展名的总大小 find "$path" -type f -printf '%s %f\n' | awk ' { size = $1 filename = $2 # 处理扩展名逻辑 if (filename ~ /^\./) { # 处理隐藏文件 split_count = split(filename, parts, ".") if (split_count == 2) { # 比如.bashrc,只有一个点,算无扩展名 ext = "(no extension)" } else { # 比如.tar.gz,取最后一个后缀 ext = parts[split_count] } } else { # 处理普通文件 split_count = split(filename, parts, ".") ext = (split_count == 1) ? "(no extension)" : parts[split_count] } # 累加大小 total[ext] += size } END { # 输出结果,转换成KB并排序 for (ext in total) { printf "%d KB %s\n", total[ext]/1024, ext } }' | sort -n
这个版本只调用一次find,所有统计逻辑都在awk里完成,不仅效率更高,还能处理更多边缘情况(比如带多个后缀的文件、隐藏文件等)。
效果验证
运行修改后的脚本,输入路径后,会输出按KB大小从小到大排序的结果,比如:
12 KB txt 45 KB png 120 KB (no extension) 567 KB pdf
内容的提问来源于stack exchange,提问作者signal7




