├── README.md ├── .gitignore ├── lib ├── logUtils.js ├── replaceString.js └── scanPorts.js ├── package.json └── index.js /README.md: -------------------------------------------------------------------------------- 1 | # npm-yptools 2 | yptools 是一个命令行工具集,批量处理图片、翻译、图片尺寸,懒人必备,提高工作效率。 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | npm-debug.log* 3 | yarn-debug.log* 4 | yarn-error.log 5 | 6 | /dist/ 7 | /build/ 8 | /lib/ 9 | /coverage/ 10 | 11 | .env 12 | .env.local 13 | .env.development 14 | .env.test 15 | .env.production 16 | 17 | .DS_Store 18 | Thumbs.db 19 | .vscode/ 20 | 21 | *.log 22 | -------------------------------------------------------------------------------- /lib/logUtils.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | // 定义颜色 4 | const colors = { 5 | normal: chalk.white, // 正常颜色(默认) 6 | warning: chalk.yellow, // 警告颜色(黄色) 7 | success: chalk.green, // 成功颜色(绿色) 8 | error: chalk.red, // 失败颜色(红色) 9 | }; 10 | 11 | // 封装打印函数 12 | const logNormal = (message) => { 13 | console.log(colors.normal(message)); 14 | }; 15 | 16 | const logWarning = (message) => { 17 | console.log(colors.warning(message)); 18 | }; 19 | 20 | const logSuccess = (message) => { 21 | console.log(colors.success(message)); 22 | }; 23 | 24 | const logError = (message) => { 25 | console.log(colors.error(message)); 26 | }; 27 | 28 | // 导出模块 29 | export { 30 | logNormal, 31 | logWarning, 32 | logSuccess, 33 | logError, 34 | }; 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chstools", 3 | "version": "1.0.2", 4 | "description": "chstools 是一个命令行工具集,批量处理图片、翻译、图片尺寸,懒人必备,提高工作效率。", 5 | "main": "index.js", 6 | "type": "module", 7 | "bin": { 8 | "chstools": "index.js" 9 | }, 10 | "files": [ 11 | "index.js", 12 | "lib", 13 | "README.md" 14 | ], 15 | "scripts": { 16 | "test": "echo \"Error: no test specified\" && exit 1", 17 | "postinstall": "npm install sharp yargs chalk" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/HansenCCC/chstools.git" 22 | }, 23 | "keywords": [ 24 | "command-line", 25 | "批量处理", 26 | "批量修改图片尺寸", 27 | "批量翻译", 28 | "批量修改文本" 29 | ], 30 | "author": "Hansen", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/HansenCCC/chstools/issues" 34 | }, 35 | "homepage": "https://github.com/HansenCCC/chstools#readme", 36 | "dependencies": { 37 | "chalk": "^5.4.1", 38 | "ping": "^0.4.4", 39 | "sharp": "^0.33.5", 40 | "yargs": "^17.7.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { scanPorts } from './lib/scanPorts.js'; 3 | import yargs from 'yargs'; 4 | import { hideBin } from 'yargs/helpers'; // 需要从 helpers 导入 hideBin 5 | import { replaceStringInFiles } from './lib/replaceString.js'; 6 | import { scanNetworkDevices } from './lib/scanWifi.js'; 7 | 8 | const commandYargs = yargs(hideBin(process.argv)) 9 | .command('scanport ', '扫描 IP 地址的指定端口范围', (yargs) => { 10 | yargs.positional('host', { 11 | description: '目标主机地址', 12 | type: 'string', 13 | }) 14 | .positional('ports', { 15 | description: '端口范围,格式如 1-65535 或单个端口如 8080', 16 | type: 'string', 17 | }); 18 | }, (argv) => { 19 | scanPorts(argv); 20 | }) 21 | .command('replace ', '替换指定目录下所有文件中的字符串', (yargs) => { 22 | yargs.positional('dir', { 23 | description: '目标目录', 24 | type: 'string', 25 | }) 26 | .positional('oldStr', { 27 | description: '需要替换的旧字符串', 28 | type: 'string', 29 | }) 30 | .positional('newStr', { 31 | description: '新的字符串', 32 | type: 'string', 33 | }); 34 | }, (argv) => { 35 | replaceStringInFiles(argv); 36 | }) 37 | .command('scanwifi', '扫描当前 Wi-Fi 下的所有设备', async () => { 38 | await scanNetworkDevices(); 39 | }) 40 | .help() 41 | .alias('help', 'h') 42 | .argv; -------------------------------------------------------------------------------- /lib/replaceString.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import readline from 'readline'; 4 | import { logNormal, logSuccess, logError, logWarning } from './logUtils.js'; 5 | 6 | // 替换目录下所有文件中的字符串,包括子目录 7 | export function replaceStringInFiles(argv) { 8 | const { dir, oldStr, newStr } = argv; 9 | 10 | logNormal(`开始扫描${dir}目录下,查找包含字符串 "${oldStr}" 的文件`); 11 | const filesToModify = []; 12 | 13 | // 递归遍历目录 14 | function traverseDirectory(directory, callback) { 15 | fs.readdir(directory, (err, files) => { 16 | if (err) { 17 | logError(`读取目录失败: ${err}`); 18 | return; 19 | } 20 | 21 | let fileCount = files.length; 22 | if (fileCount === 0) { 23 | logNormal('目录为空'); 24 | callback(); 25 | return; 26 | } 27 | 28 | files.forEach((file) => { 29 | const filePath = path.join(directory, file); 30 | 31 | fs.stat(filePath, (err, stats) => { 32 | if (err) { 33 | logError(`检查文件状态失败: ${err}`); 34 | return; 35 | } 36 | 37 | if (stats.isFile()) { 38 | // 如果是文件,读取文件内容并检查是否需要替换 39 | fs.readFile(filePath, 'utf8', (err, data) => { 40 | if (err) { 41 | logError(`读取文件失败: ${err}`); 42 | return; 43 | } 44 | 45 | const updatedData = data.replace(new RegExp(oldStr, 'g'), newStr); 46 | 47 | if (updatedData !== data) { 48 | filesToModify.push(filePath); // 记录需要修改的文件 49 | } 50 | 51 | fileCount--; 52 | if (fileCount === 0) { 53 | callback(); // 所有文件扫描完毕后,执行回调 54 | } 55 | }); 56 | } else if (stats.isDirectory()) { 57 | // 如果是目录,递归调用 58 | traverseDirectory(filePath, () => { 59 | fileCount--; 60 | if (fileCount === 0) { 61 | callback(); // 所有文件扫描完毕后,执行回调 62 | } 63 | }); 64 | } 65 | }); 66 | }); 67 | }); 68 | } 69 | 70 | // 开始遍历目标目录 71 | traverseDirectory(dir, () => handleUserConfirmation(filesToModify, oldStr, newStr)); 72 | } 73 | 74 | // 用户确认修改操作 75 | function handleUserConfirmation(filesToModify, oldStr, newStr) { 76 | if (filesToModify.length === 0) { 77 | logNormal('没有找到需要修改的文件'); 78 | return; 79 | } 80 | 81 | logNormal('即将修改的文件:'); 82 | filesToModify.forEach((file, index) => { 83 | logWarning(`${index + 1}. ${file}`); 84 | }); 85 | 86 | const rl = readline.createInterface({ 87 | input: process.stdin, 88 | output: process.stdout, 89 | }); 90 | 91 | // 确认一次 92 | rl.question(`是否将 "${oldStr}" 全部修改为 "${newStr}" ?y/n:`, (answer) => { 93 | if (answer.toLowerCase() === 'y') { 94 | // 如果用户同意,替换所有文件中的内容 95 | filesToModify.forEach((filePath) => { 96 | fs.readFile(filePath, 'utf8', (err, data) => { 97 | if (err) { 98 | logError(`读取文件失败: ${err}`); 99 | return; 100 | } 101 | 102 | const updatedData = data.replace(new RegExp(oldStr, 'g'), newStr); 103 | 104 | fs.writeFile(filePath, updatedData, 'utf8', (err) => { 105 | if (err) { 106 | logError(`写入文件失败: ${err}`); 107 | return; 108 | } 109 | logSuccess(`成功替换文件: ${filePath}`); 110 | }); 111 | }); 112 | }); 113 | } else { 114 | logNormal('操作已取消'); 115 | } 116 | rl.close(); 117 | }); 118 | } 119 | -------------------------------------------------------------------------------- /lib/scanPorts.js: -------------------------------------------------------------------------------- 1 | import net from 'net'; 2 | import { logNormal, logSuccess, logError, logWarning } from './logUtils.js'; 3 | 4 | export function scanPorts(argv) { 5 | // 提取端口范围 6 | const [startPort, endPort] = argv.ports.split('-').map(Number); 7 | // 如果端口范围格式错误(没有 - 或者端口值无效) 8 | if (argv.ports.includes('-') && (isNaN(startPort) || isNaN(endPort))) { 9 | logWarning('端口范围无效,请使用正确的格式,如 1-65535'); 10 | return; 11 | } 12 | if (!argv.ports.includes('-') && !isNaN(startPort)) { 13 | startScanPorts(argv.host, startPort, startPort); 14 | } else if (argv.ports.includes('-')) { 15 | startScanPorts(argv.host, startPort, endPort); 16 | } else { 17 | logWarning('无效的端口范围,正确格式为 1-65535 或单个端口'); 18 | } 19 | } 20 | 21 | function startScanPorts(host, startPort = 1, endPort = 65535) { 22 | logNormal(`开始扫描 ${host} 的端口,范围是${startPort}~${endPort}`); 23 | 24 | // 每批扫描的端口数 25 | const batchSize = 100; // 可以根据需要调整为更合适的数值 26 | 27 | // 初始化计数器 28 | let totalPorts = 0; 29 | let openPorts = 0; 30 | let successPorts = 0; 31 | let failedPorts = 0; 32 | 33 | // 存储开放的端口 34 | const openPortsList = []; 35 | 36 | // 开始时间 37 | const startTime = Date.now(); 38 | 39 | // 计算总的批次数量 40 | const totalBatches = Math.ceil((endPort - startPort + 1) / batchSize); 41 | 42 | const scanBatch = (batchStartPort, batchEndPort) => { 43 | return new Promise((resolve) => { 44 | const promises = []; 45 | for (let port = batchStartPort; port <= batchEndPort; port++) { 46 | totalPorts++; 47 | const socketPromise = new Promise((resolve, reject) => { 48 | const socket = new net.Socket(); 49 | socket.setTimeout(3000); 50 | socket.on('connect', () => { 51 | successPorts++; 52 | logSuccess(`${port} 开放`); 53 | openPorts++; 54 | openPortsList.push(port); // 将开放的端口添加到数组中 55 | socket.destroy(); 56 | resolve(); 57 | }); 58 | socket.on('timeout', () => { 59 | socket.destroy(); 60 | successPorts++; 61 | failedPorts++; 62 | resolve(); 63 | }); 64 | socket.on('error', (err) => { 65 | socket.destroy(); 66 | successPorts++; 67 | failedPorts++; 68 | resolve(); 69 | }); 70 | socket.connect(port, host); 71 | }); 72 | promises.push(socketPromise); 73 | } 74 | 75 | // 等待当前批次的扫描完成 76 | Promise.all(promises) 77 | .then(() => resolve()); 78 | }); 79 | }; 80 | 81 | // 扫描每个批次 82 | (async function scanInBatches() { 83 | for (let i = 0; i < totalBatches; i++) { 84 | const batchStartPort = startPort + i * batchSize; 85 | const batchEndPort = Math.min(startPort + (i + 1) * batchSize - 1, endPort); 86 | 87 | await scanBatch(batchStartPort, batchEndPort); // 等待每一批次完成 88 | logWarning(`${i + 1} > 扫描端口范围: ${batchStartPort}~${batchEndPort}`); 89 | } 90 | 91 | // 计算总耗时 92 | const endTime = Date.now(); 93 | const duration = (endTime - startTime) / 1000; // 转换为秒 94 | 95 | logNormal('==============================================='); 96 | logNormal(`IP:${host}`); 97 | logNormal(`端口范围:${startPort}~${endPort}`); 98 | logNormal(`总共扫描: ${totalPorts} 个端口`); 99 | logNormal(`开放的端口:${openPorts}`); 100 | logNormal(`总耗时:${duration} 秒`); 101 | // 打印所有开放的端口 102 | if (openPortsList.length > 0) { 103 | logSuccess(`开放的端口:${openPortsList.join(', ')}`); 104 | } else { 105 | logNormal('没有开放的端口'); 106 | } 107 | logNormal('==============================================='); 108 | })(); 109 | } 110 | --------------------------------------------------------------------------------