特定文件夹搜索工具排序异常问题(含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
相关产品推荐
相关产品推荐

