+4
-0
@@ -139,2 +139,6 @@ #!/usr/bin/env node | ||
| .option('-p, --prefix <string>', '自定义前缀,默认P', 'P') | ||
| .option('-d, --dir', '递归处理子目录') | ||
| .option('-num, --number <number>', '序号位数,默认5位', 5) | ||
| .option('-type, --type <types...>', '文件类型,支持多个类型,如: jpg png gif') | ||
| .option('-forceDir, --forceDir', '强制重命名文件夹,从1开始自增') | ||
| .action((options, command) => { | ||
@@ -141,0 +145,0 @@ const cmd = command.args[0]; |
+84
-11
@@ -30,2 +30,6 @@ # 文件重命名模块 (rename.js) | ||
| | --prefix | -p | 字符串 | 自定义前缀,默认P | | ||
| | --dir | -d | 无 | 递归处理子目录 | | ||
| | --number | -num | 数字 | 序号位数,默认5位 | | ||
| | --type | -type | <types...> | 文件类型,支持多个类型,如: jpg png gif,不区分大小写,jpg和jpeg视为同一类型 | | ||
| | --forceDir | 无 | 无 | 强制重命名文件夹,从1开始自增,先处理子目录再处理父目录 | | ||
@@ -54,21 +58,90 @@ ## 5. 使用示例 | ||
| ### 5.3 按文件类型重命名 | ||
| ```bash | ||
| daji rn -p p -type jpg | ||
| ``` | ||
| 执行此命令后,工具会将当前目录下的所有jpg文件按照p00001.jpg、p00002.jpg的格式重命名。 | ||
| ### 5.4 多类型文件重命名 | ||
| ```bash | ||
| daji rn -p p -type jpg png gif | ||
| ``` | ||
| 执行此命令后,工具会将当前目录下的所有jpg、png、gif文件按照p00001.jpg、p00002.png、p00003.gif的格式重命名。 | ||
| ### 5.5 自定义序号位数 | ||
| ```bash | ||
| daji rn -p p -num 4 | ||
| ``` | ||
| 执行此命令后,工具会将当前目录下的文件按照p0001、p0002的4位数格式重命名。 | ||
| ### 5.6 递归处理子目录 | ||
| ```bash | ||
| daji rn -p p -d -type jpg | ||
| ``` | ||
| 执行此命令后,工具会递归处理当前目录及其子目录下的所有jpg文件,每个目录内的文件从p00001.jpg开始编号。 | ||
| ### 5.7 结合多个选项 | ||
| ```bash | ||
| daji rn -p p -d -type jpg png -num 4 | ||
| ``` | ||
| 执行此命令后,工具会递归处理当前目录及其子目录下的所有jpg、png文件,每个目录内的文件从p0001.jpg开始编号(4位数)。 | ||
| ### 5.8 重命名文件夹 | ||
| ```bash | ||
| daji rn -forceDir | ||
| ``` | ||
| 执行此命令后,工具会将当前目录下的文件夹按照1、2、3的格式重命名,先处理子目录再处理父目录。 | ||
| ### 5.9 结合文件和文件夹重命名 | ||
| ```bash | ||
| daji rn -p p -type jpg -forceDir | ||
| ``` | ||
| 执行此命令后,工具会同时重命名当前目录下的jpg文件和文件夹,文件按p00001.jpg格式,文件夹按1、2、3格式。 | ||
| ## 6. 实现原理 | ||
| 1. **获取文件列表**: | ||
| - 读取当前目录下的所有文件 | ||
| - 过滤出文件,排除目录 | ||
| - 按文件名排序 | ||
| 1. **获取文件和文件夹列表**: | ||
| - 读取当前目录下的所有文件和文件夹 | ||
| - 如果指定了 `-d` 选项,则递归处理子目录 | ||
| - 分别收集文件和文件夹列表 | ||
| 2. **构建新文件名**: | ||
| 2. **文件类型过滤**: | ||
| - 如果指定了 `-type` 选项,按类型过滤文件 | ||
| - 统一转换为小写,jpg和jpeg视为同一类型 | ||
| - 不区分文件类型的大小写 | ||
| 3. **构建新文件名**: | ||
| - 使用指定前缀(默认P) | ||
| - 生成5位递增序号(不足补零) | ||
| - 生成指定位数的递增序号(默认5位,不足补零) | ||
| - 保留原文件的扩展名 | ||
| 3. **执行重命名**: | ||
| - 遍历文件列表 | ||
| - 逐个执行重命名操作 | ||
| - 输出重命名结果 | ||
| 4. **执行文件重命名**: | ||
| - 按目录分组文件 | ||
| - 每个目录内的文件独立编号 | ||
| - 按文件名排序后逐个执行重命名 | ||
| - 输出重命名过程 | ||
| 4. **统计结果**: | ||
| 5. **执行文件夹重命名**: | ||
| - 如果指定了 `-forceDir` 选项,重命名文件夹 | ||
| - 按路径长度排序,确保先处理子目录 | ||
| - 文件夹名称从1开始自增 | ||
| - 输出重命名过程 | ||
| 6. **统计结果**: | ||
| - 统计成功和失败的文件数量 | ||
| - 统计成功和失败的文件夹数量 | ||
| - 输出最终结果 | ||
@@ -75,0 +148,0 @@ |
+200
-27
@@ -8,2 +8,163 @@ const fs = require('fs').promises; | ||
| /** | ||
| * 递归获取目录下的所有文件和文件夹 | ||
| * @param {string} dirPath - 目录路径 | ||
| * @param {boolean} recursive - 是否递归处理子目录 | ||
| * @returns {Promise<Object>} 包含文件和文件夹的对象 | ||
| */ | ||
| async function getAllFilesAndDirs(dirPath, recursive) { | ||
| const entries = await fs.readdir(dirPath, { withFileTypes: true }); | ||
| let result = { | ||
| files: [], | ||
| dirs: [] | ||
| }; | ||
| for (const entry of entries) { | ||
| const fullPath = path.join(dirPath, entry.name); | ||
| if (entry.isDirectory()) { | ||
| result.dirs.push(fullPath); | ||
| if (recursive) { | ||
| const subResult = await getAllFilesAndDirs(fullPath, recursive); | ||
| result.files = [...result.files, ...subResult.files]; | ||
| result.dirs = [...result.dirs, ...subResult.dirs]; | ||
| } | ||
| } else { | ||
| result.files.push(fullPath); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * 按目录分组文件 | ||
| * @param {string[]} filePaths - 文件路径列表 | ||
| * @returns {Object} 按目录分组的文件 | ||
| */ | ||
| function groupFilesByDir(filePaths) { | ||
| const groups = {}; | ||
| for (const filePath of filePaths) { | ||
| const dir = path.dirname(filePath); | ||
| if (!groups[dir]) { | ||
| groups[dir] = []; | ||
| } | ||
| groups[dir].push(filePath); | ||
| } | ||
| return groups; | ||
| } | ||
| /** | ||
| * 重命名文件或文件夹 | ||
| * @param {string} oldPath - 原路径 | ||
| * @param {string} newPath - 新路径 | ||
| * @returns {Promise<boolean>} 重命名是否成功 | ||
| */ | ||
| async function renameItem(oldPath, newPath) { | ||
| try { | ||
| await fs.rename(oldPath, newPath); | ||
| log.success(`${path.basename(oldPath)} → ${path.basename(newPath)}`); | ||
| return true; | ||
| } catch (error) { | ||
| log.error(`重命名 ${path.basename(oldPath)} 失败: ${error.message}`); | ||
| return false; | ||
| } | ||
| } | ||
| /** | ||
| * 重命名目录下的文件 | ||
| * @param {string} dirPath - 目录路径 | ||
| * @param {string[]} filePaths - 该目录下的文件路径 | ||
| * @param {Object} options - 命令选项 | ||
| * @returns {Promise<Object>} 重命名结果 | ||
| */ | ||
| async function renameFilesInDir(dirPath, filePaths, options) { | ||
| const { prefix = 'P', number = 5 } = options; | ||
| // 按文件名排序 | ||
| const sortedFiles = filePaths.sort(); | ||
| if (sortedFiles.length === 0) { | ||
| log.tip(`${dirPath} 目录下没有需要重命名的文件`); | ||
| return { success: 0, fail: 0 }; | ||
| } | ||
| log.info(`\n处理目录: ${dirPath}`); | ||
| log.info(`找到 ${sortedFiles.length} 个文件,准备重命名...`); | ||
| let successCount = 0; | ||
| let failCount = 0; | ||
| // 开始重命名 | ||
| for (let i = 0; i < sortedFiles.length; i++) { | ||
| const oldPath = sortedFiles[i]; | ||
| const ext = path.extname(oldPath); | ||
| const newName = `${prefix}${String(i + 1).padStart(number, '0')}${ext}`; | ||
| const newPath = path.join(dirPath, newName); | ||
| if (await renameItem(oldPath, newPath)) { | ||
| successCount++; | ||
| } else { | ||
| failCount++; | ||
| } | ||
| } | ||
| return { success: successCount, fail: failCount }; | ||
| } | ||
| /** | ||
| * 重命名文件夹 | ||
| * @param {string[]} dirPaths - 文件夹路径列表 | ||
| * @returns {Promise<Object>} 重命名结果 | ||
| */ | ||
| async function renameDirs(dirPaths) { | ||
| // 按路径长度排序,确保先处理子目录 | ||
| const sortedDirs = dirPaths.sort((a, b) => b.split(path.sep).length - a.split(path.sep).length); | ||
| let successCount = 0; | ||
| let failCount = 0; | ||
| log.info(`\n准备重命名 ${sortedDirs.length} 个文件夹...`); | ||
| for (let i = 0; i < sortedDirs.length; i++) { | ||
| const oldPath = sortedDirs[i]; | ||
| const parentDir = path.dirname(oldPath); | ||
| const newName = String(i + 1); | ||
| const newPath = path.join(parentDir, newName); | ||
| if (await renameItem(oldPath, newPath)) { | ||
| successCount++; | ||
| } else { | ||
| failCount++; | ||
| } | ||
| } | ||
| return { success: successCount, fail: failCount }; | ||
| } | ||
| /** | ||
| * 过滤文件类型 | ||
| * @param {string[]} filePaths - 文件路径列表 | ||
| * @param {string[]} types - 文件类型数组 | ||
| * @returns {string[]} 过滤后的文件路径 | ||
| */ | ||
| function filterFilesByType(filePaths, types) { | ||
| if (!types || types.length === 0) { | ||
| return filePaths; | ||
| } | ||
| // 处理类型:统一转为小写,合并jpg和jpeg | ||
| const normalizedTypes = types.map(type => { | ||
| const lowerType = type.toLowerCase(); | ||
| return lowerType === 'jpeg' ? 'jpg' : lowerType; | ||
| }); | ||
| return filePaths.filter(filePath => { | ||
| const ext = path.extname(filePath).toLowerCase().replace('.', ''); | ||
| const normalizedExt = ext === 'jpeg' ? 'jpg' : ext; | ||
| return normalizedTypes.includes(normalizedExt); | ||
| }); | ||
| } | ||
| /** | ||
| * 重命名当前目录下的文件 | ||
@@ -14,3 +175,9 @@ * @param {string} cmd - 命令参数 | ||
| async function renameTools(cmd, options) { | ||
| const { prefix = 'P' } = options; | ||
| const { | ||
| prefix = 'P', | ||
| dir: recursive = false, | ||
| number = 5, | ||
| type: types = [], | ||
| forceDir = false | ||
| } = options; | ||
@@ -20,42 +187,48 @@ try { | ||
| const currentDir = process.cwd(); | ||
| log.info(`当前目录: ${currentDir}`); | ||
| log.info(`当前工作目录: ${currentDir}`); | ||
| // 获取当前目录下的所有文件 | ||
| const files = await fs.readdir(currentDir, { withFileTypes: true }); | ||
| // 显示处理的文件类型 | ||
| if (types && types.length > 0) { | ||
| log.info(`只处理以下类型的文件: ${types.join(', ')}`); | ||
| } | ||
| // 过滤出文件(排除目录) | ||
| const fileList = files | ||
| .filter(file => file.isFile()) | ||
| .map(file => file.name) | ||
| .sort(); // 按文件名排序 | ||
| // 获取所有文件和文件夹 | ||
| const allItems = await getAllFilesAndDirs(currentDir, recursive); | ||
| if (fileList.length === 0) { | ||
| log.tip('当前目录下没有文件需要重命名'); | ||
| // 过滤文件类型 | ||
| const filteredFiles = filterFilesByType(allItems.files, types); | ||
| if (filteredFiles.length === 0 && !forceDir) { | ||
| log.tip('没有找到符合条件的文件需要重命名'); | ||
| return; | ||
| } | ||
| log.info(`找到 ${fileList.length} 个文件,准备重命名...`); | ||
| // 按目录分组文件 | ||
| const filesByDir = groupFilesByDir(filteredFiles); | ||
| console.log('=============================='); | ||
| // 开始重命名 | ||
| let successCount = 0; | ||
| let failCount = 0; | ||
| // 开始重命名文件 | ||
| let totalSuccess = 0; | ||
| let totalFail = 0; | ||
| for (let i = 0; i < fileList.length; i++) { | ||
| const oldName = fileList[i]; | ||
| const ext = path.extname(oldName); | ||
| const newName = `${prefix}${String(i + 1).padStart(5, '0')}${ext}`; | ||
| if (filteredFiles.length > 0) { | ||
| log.info(`找到 ${filteredFiles.length} 个文件,分布在 ${Object.keys(filesByDir).length} 个目录中`); | ||
| try { | ||
| await fs.rename(path.join(currentDir, oldName), path.join(currentDir, newName)); | ||
| log.success(`${oldName} → ${newName}`); | ||
| successCount++; | ||
| } catch (error) { | ||
| log.error(`重命名 ${oldName} 失败: ${error.message}`); | ||
| failCount++; | ||
| for (const [dirPath, filePaths] of Object.entries(filesByDir)) { | ||
| const result = await renameFilesInDir(dirPath, filePaths, options); | ||
| totalSuccess += result.success; | ||
| totalFail += result.fail; | ||
| } | ||
| } | ||
| // 重命名文件夹 | ||
| if (forceDir && allItems.dirs.length > 0) { | ||
| const dirResult = await renameDirs(allItems.dirs); | ||
| totalSuccess += dirResult.success; | ||
| totalFail += dirResult.fail; | ||
| } | ||
| console.log('=============================='); | ||
| log.info(`重命名完成!成功: ${successCount} 个,失败: ${failCount} 个`); | ||
| log.info(`重命名完成!总成功: ${totalSuccess} 个,总失败: ${totalFail} 个`); | ||
@@ -62,0 +235,0 @@ } catch (error) { |
+1
-1
| { | ||
| "name": "daji", | ||
| "version": "2.0.1", | ||
| "version": "2.0.2", | ||
| "description": "", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
107144
7.53%2416
6.62%