You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

特定文件夹搜索工具排序异常问题(含Bash/JS实现)

目录搜索工具排序问题修复

问题概述

现有一款可搜索指定文件夹(默认node_modules)的工具,包含Bash和JavaScript两个版本,支持按大小、修改日期、路径对目录分类。当前按大小排序时无法实现从大到小的降序排列,需排查修复。


Bash版本修复

问题原因

原代码依赖格式化后的字符串(如"1.2 GB"、"500 MB")排序,字符串按字符顺序比较会导致数值逻辑错误(比如"100 MB"会被判定为比"2 GB"大);同时sort命令的字段索引易因路径含引号偏移,稳定性差。

修复方案

  • 存储原始size_kb数值用于排序,避免字符串比较误差
  • 排序后再格式化大小输出,保证数值逻辑正确

修复后代码

#!/bin/bash

start_time=$(date +%s.%N)

find_dir="node_modules"
sort_by="path"

while [[ "$1" =~ ^- ]]; do
  case $1 in
    -t|--target)
      find_dir="$2"
      shift 2
      ;;
    -s|--sort)
      sort_by="$2"
      shift 2
      ;;
    *)
      echo "Invalid option: $1"
      exit 1
      ;;
  esac
done

dirs=$(find $(pwd) -type d -name "$find_dir" 2>/dev/null)
total_size_kb=0

# 存储原始数据:每行格式为 "size_kb|path|last_mod"
declare -a results

for dir in $dirs; do
  parent_dir=$(dirname "$dir")
  
  if [[ ! "$parent_dir" =~ /$find_dir/ ]]; then
    last_mod=$(stat -f "%Sm" -t "%d/%m/%Y %H:%M:%S" "$dir")
    size_kb=$(du -sk "$dir" | awk '{print $1}')
    
    total_size_kb=$((total_size_kb + size_kb))
    # 存储原始数值和信息,用|分隔避免路径空格干扰
    results+=("$size_kb|$dir|$last_mod")
  fi
done

# 根据排序规则处理原始数据
if [[ "$sort_by" == "size" ]]; then
  # 按size_kb降序排序
  sorted_results=$(printf "%s\n" "${results[@]}" | sort -t'|' -k1,1nr)
elif [[ "$sort_by" == "path" ]]; then
  # 按路径升序排序
  sorted_results=$(printf "%s\n" "${results[@]}" | sort -t'|' -k2,2)
elif [[ "$sort_by" == "last-mod" ]]; then
  # 按修改日期降序排序(转换为时间戳比较)
  sorted_results=$(printf "%s\n" "${results[@]}" | sort -t'|' -k3,3r)
fi

# 格式化输出为JSON格式
json_paths=""
while IFS='|' read -r size_kb path last_mod; do
  # 格式化大小显示
  size_mb=$(echo "scale=2; $size_kb/1024" | bc)
  if (( $(echo "$size_mb < 1" | bc -l) )); then
    size=$(echo "scale=2; $size_kb" | bc)
    size="${size} KB"
  elif (( $(echo "$size_mb >= 1024" | bc -l) )); then
    size=$(echo "scale=2; $size_mb/1024" | bc)
    size="${size} GB"
  else
    size="${size_mb} MB"
  fi
  # 拼接JSON条目
  json_paths+="{\"path\": \"$path\", \"last_mod\": \"$last_mod\", \"size\": \"$size\"},"
done <<< "$sorted_results"

# 移除最后一个逗号
json_paths=${json_paths%,}

end_time=$(date +%s.%N)
elapsed_time=$(echo "$end_time - $start_time" | bc)
total_size_mb=$(echo "scale=2; $total_size_kb/1024" | bc)

# 生成最终JSON
json="{
  \"releasable_space\": \"${total_size_mb} MB\", 
  \"search_completed\": \"$(echo $elapsed_time | cut -d'.' -f1)s\",
  \"paths\": [$json_paths]
}"

echo "$json"

JavaScript版本修复

问题原因

Windows平台下目录大小计算逻辑错误:原代码使用dir /s /a命令提取"bytes free",这是磁盘剩余空间而非目录大小,导致size数值完全错误,排序自然失效。

修复方案

  • Windows平台改用PowerShell命令计算目录大小,保证数值准确
  • 保留原有的排序逻辑(基于原始size_kb数值),确保降序正确

修复后代码

const fs = require('fs').promises;
const path = require('path');
const { execSync } = require('child_process');

const startTime = process.hrtime.bigint();
let targetDir = 'node_modules';
let sortBy = 'path';
let searchPath = '.';
let maxDepth = 5;
let saveFile = null;
let noMaxDepth = false;

const args = process.argv.slice(2);
args.forEach((arg, i) => {
  if (arg === '-t' || arg === '--target') targetDir = args[i + 1];
  if (arg === '-s' || arg === '--sort') sortBy = args[i + 1];
  if (arg === '-p' || arg === '--path') searchPath = args[i + 1];
  if (arg === '--save') saveFile = args[i + 1];
  if (arg === '--maxDepth') maxDepth = parseInt(args[i + 1], 10);
  if (arg === '--noMaxDepth') noMaxDepth = true;
});

(async () => {
  if (isNaN(maxDepth) || maxDepth < 0) {
    console.error("错误:--maxDepth 参数无效,应为正整数");
    process.exit(1);
  }

  const validSortOptions = ['path', 'size', 'last-mod'];
  if (!validSortOptions.includes(sortBy)) {
    console.error(`错误:无效的排序选项,可选值为:${validSortOptions.join(", ")}`);
    process.exit(1);
  }

  try {
    await fs.access(searchPath);
  } catch (error) {
    console.error(`错误:搜索路径 '${searchPath}' 不存在或无法访问`);
    process.exit(1);
  }

  if (noMaxDepth) maxDepth = Infinity;

  const formatDate = (date) => {
    const day = String(date.getDate()).padStart(2, '0');
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const year = date.getFullYear();
    const hours = String(date.getHours()).padStart(2, '0');
    const minutes = String(date.getMinutes()).padStart(2, '0');
    const seconds = String(date.getSeconds()).padStart(2, '0');
    return `${day}/${month}/${year} ${hours}:${minutes}:${seconds}`;
  };

  const formatSize = (sizeInKB) => {
    if (sizeInKB < 1024) {
      return `${sizeInKB} KB`;
    }

    const sizeInMB = sizeInKB / 1024;
    if (sizeInMB < 1024) {
      return `${sizeInMB.toFixed(2)} MB`;
    }

    const sizeInGB = sizeInMB / 1024;
    return `${sizeInGB.toFixed(2)} GB`;
  };

  const formatTime = (elapsedTime) => {
    const seconds = Number(elapsedTime) / 1e9;
    if (seconds < 60) {
      return `${seconds.toFixed(2)}s`;
    }

    const minutes = seconds / 60;
    if (minutes < 60) {
      return `${minutes.toFixed(2)}m`;
    }

    const hours = minutes / 60;
    return `${hours.toFixed(2)}h`;
  };

  const getDaysDifference = (lastModDate) => {
    const today = new Date();
    const diffTime = today - new Date(lastModDate);
    const diffDays = Math.floor(diffTime / (1000 * 3600 * 24));
    return diffDays;
  };

  const getDirectories = async (dirPath, depth = 0) => {
    if (maxDepth !== Infinity && depth > maxDepth) return [];

    const dirs = [];
    try {
      const items = await fs.readdir(dirPath);
      for (const item of items) {
        const fullPath = path.resolve(dirPath, item);
        try {
          const stat = await fs.stat(fullPath);
          if (stat.isDirectory()) {
            dirs.push(fullPath);
            if (!fullPath.includes(targetDir)) {
              dirs.push(...await getDirectories(fullPath, depth + 1));
            }
          }
        } catch (e) {
          // 忽略无法读取的目录
        }
      }
    } catch (err) {
      // 忽略无法读取的路径
    }
    return dirs;
  };

  const getDirInfo = async (dir) => {
    let size = 0;
    const isWindows = process.platform === 'win32';

    if (isWindows) {
      try {
        // 使用PowerShell计算目录大小(单位:字节),转换为KB
        const result = execSync(`powershell -Command "(Get-ChildItem '${dir}' -Recurse | Measure-Object -Property Length -Sum).Sum"`).toString().trim();
        size = Math.round(parseInt(result) / 1024); // 转换为KB
      } catch (e) {
        console.error(`计算目录大小失败 ${dir}:`, e);
      }
    } else {
      try {
        const result = execSync(`du -sk "${dir}"`).toString();
        size = parseInt(result.split("\t")[0]);
      } catch (e) {
        console.error(`计算目录大小失败 ${dir}:`, e);
      }
    }

    const lastMod = await fs.stat(dir);
    const lastModDate = lastMod.mtime;
    const lastModDay = getDaysDifference(lastModDate);

    return { path: dir, size, lastMod: formatDate(lastModDate), lastModDay };
  };

  const spinner = ["⠙", "⠘", "⠰", "⠴", "⠤", "⠦", "⠆", "⠃", "⠋", "⠉"];
  let spinIndex = 0;

  const displaySpinner = () => {
    process.stdout.write(`\r加载中 ${spinner[spinIndex]} `);
    spinIndex = (spinIndex + 1) % spinner.length;
  };

  const spinnerInterval = setInterval(displaySpinner, 100);

  const dirs = (await getDirectories(searchPath))
    .filter((dir) => dir.includes(targetDir))
    .map(async (dir) => await getDirInfo(dir));

  const resolvedDirs = await Promise.all(dirs);

  const totalSizeKB = resolvedDirs.reduce((acc, dir) => acc + dir.size, 0);

  const sortedDirs = resolvedDirs.sort((a, b) => {
    if (sortBy === 'size') return b.size - a.size; // 按原始KB数值降序
    if (sortBy === 'path') return a.path.localeCompare(b.path);
    if (sortBy === 'last-mod') return new Date(b.lastMod) - new Date(a.lastMod);
    return 0;
  });

  const resultJson = {
    releasable_space: formatSize(totalSizeKB),
    search_completed: formatTime(process.hrtime.bigint() - startTime),
    num_paths: sortedDirs.length,
    paths: sortedDirs.map(dir => ({
      path: dir.path,
      last_mod: dir.lastMod,
      last_mod_day: dir.lastModDay,
      size: formatSize(dir.size)
    }))
  };

  clearInterval(spinnerInterval);
  console.log();
  console.clear();

  if (saveFile) {
    try {
      await fs.writeFile(saveFile, JSON.stringify(resultJson, null, 2), 'utf-8');
      console.log(`结果已保存至 ${saveFile}`);
      console.log({
        releasable_space: resultJson.releasable_space,
        search_completed: resultJson.search_completed,
        num_paths: resultJson.num_paths
      });
    } catch (error) {
      console.error(`保存结果失败 ${saveFile}`, error);
    }
  } else {
    console.log(resultJson);
  }
})();

使用示例

# JavaScript版本按大小降序搜索当前目录,结果保存到jsonFile.json
node main.js --sort size --path . --save jsonFile.json

# Bash版本按大小降序搜索当前目录的my_target文件夹
bash script.sh --target my_target --sort size

内容的提问来源于stack exchange,提问作者Paul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 13:54:52