You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动