diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b0d260 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +.AppleDouble +.LSOverride +/node_modules + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..308c66d --- /dev/null +++ b/Readme.md @@ -0,0 +1,15 @@ +![module info](https://nodei.co/npm/bash-ip.png?downloads=true&downloadRank=true&stars=true) + +# bash-ip +> `bash-ip` 类似于linux下的简单版的ip命令工具. 目前只实现了几个用法, 后续会慢慢完善。 + +## versions + +- 0.0.1 (2018/05/15) + * 发布0.0.1版 + * 实现 ip a[ddress], ip route [list|get|add|delete ], ip link [show ] + + + + + diff --git a/index.js b/index.js new file mode 100755 index 0000000..8bee442 --- /dev/null +++ b/index.js @@ -0,0 +1,241 @@ +#!/usr/bin/env node + +require('es.shim') +const os = require('os') +const path = require('path') +const pg = require('progress') +const cp = require('child_process') +const chalk = require('chalk') + +const print = console.log +const VERSION = '0.0.1' + +let args = process.argv.slice(2) +let af = 4 + +function run(cmd) { + try { + return cp.execSync(cmd).toString('utf8') + } catch (err) { + return false + } +} + +// 打印网卡接口信息 +function networkInterfaces() { + let result = os.networkInterfaces() + print(chalk.green.bold(os.hostname())) + for (let k in result) { + let info = '' + let base = '' + for (let it of result[k]) { + base = `MAC ${it.mac} INTERNAL ${chalk.yellow(it.internal)}` + info += `${it.family.padStart(12)} ${chalk.green(it.address)} NETMASK ${ + it.netmask + } \n` + } + print('%s %s\n%s', chalk.blue.bold((k + ':').padEnd(8)), base, info) + } +} + +function routeList() { + let result + if (af === 6) { + result = run(`netstat -nr -f inet6 2>/dev/null`) + } else { + result = run(`netstat -nr -f inet 2>/dev/null`) + } + if (result === false) { + return + } + result = result.split('\n') + result.splice(0, 3) + result[0] = chalk.blue(result[0]) + print(result.join('\n')) +} + +function routeGet(addr) { + let result = run(`route -n get ${addr}`) + let dict = {} + result = result.split('\n') + for (let it of result) { + it = it.split(':') + if (it.length < 2) { + continue + } + dict[it[0].trim().replace(/\s+/g, '_')] = it[1].trim() + } + + let { route_to, interface, gateway } = dict + let src = run( + `python -c "import socket;s = socket.socket(socket.AF_INET${ + af === 6 ? '6' : '' + }, socket.SOCK_DGRAM);s.connect(('${addr}',7));print(s.getsockname()[0])"` + ) + src = src.trim() + + print( + `${route_to} ${ + gateway ? 'via ' + gateway : '' + } dev ${interface} src ${src}` + ) +} + +function routeAdd(dev = 'dev', addr) { + if (!addr) { + return + } + let inet = 'inet' + (af === 6 ? '6' : '') + let result = run(`sudo ifconfig ${dev} ${inet} add ${addr}`) + if (result === false) { + print(chalk.red('route add failed')) + } +} + +function routeDel(dev = 'dev', addr) { + if (!addr) { + return + } + let inet = 'inet' + (af === 6 ? '6' : '') + let result = run(`sudo ifconfig ${dev} ${inet} ${addr} remove`) + if (result === false) { + print(chalk.red('route delete failed')) + } +} + +function parseIfconfigArr(arr) { + let i = 0 + + for (let k of arr) { + k = k.split(/\s{1,2}/) + if (k.length < 3) { + continue + } + if (i === 0) { + i = 1 + k[0] = chalk.blue.bold(k[0].padEnd(8)) + } else { + k.shift() + if (k.length === 2) { + k[0] = k[0].padStart(8) + } else { + if (k[0] === '') { + k[0] = ' '.repeat(8) + } + if (k[1] === '') { + k[1] = ' '.repeat(7) + } + if (k[3] === '' && k[4] === '') { + k.splice(3, 2) + } + if (k[2] === '') { + k.splice(2, 1) + } + if (['ether', 'inet', 'inet6'].includes(k[1])) { + k[2] = chalk.green(k[2]) + } else if ('status:' === k[1]) { + k[2] = chalk.yellow(k[2]) + } + } + } + + print(k.join(' ')) + } +} + +function doIfconfig() { + let result = run('ifconfig') + .replace(/\t/g, ' ') + .trim() + + result = result.replace(/([\w]+: flags=.*\n)/g, '=====$1').split('=====') + !result[0] && result.shift() + + for (let it of result) { + it = it.split('\n') + parseIfconfigArr(it) + print('\n') + } +} + +function doIfconfigOne(flag) { + let result = run(`ifconfig ${flag}`) + .replace(/\t/g, ' ') + .trim() + + result = result.split('\n') + parseIfconfigArr(result) +} + +function doHelp() { + print('Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }') + print(' ', 'ip a[ddress]', '打印网卡的IP信息') + print(' ', 'ip route [add|list|delete|get]', '路由相关信息及操作') + print(' ', 'ip link', '打印网卡的详细信息') + print(' ', 'ip -V[--version]', '打印版本信息') + print(' ', 'ip -h[--help]', '打印帮助信息') + print() +} + +function printVersion() { + print('ip, v%s', VERSION) +} + +if (!args.length) { + doHelp() + process.exit() +} + +if (args[0] === '-6') { + af = 6 + args.shift() +} + +if (args[0] === '-4') { + af = 4 + args.shift() +} + +switch (args[0]) { + case 'link': + args.shift() + if (args.length === 0) { + doIfconfig() + break + } + if (args[0] === 'show' && args.length === 2) { + args.shift() + doIfconfigOne(args[0]) + break + } + break + case 'a': + case 'address': + networkInterfaces() + break + case 'route': + args.shift() + if (!args.length || ['list', 'show', 'lst', 'ls'].includes(args[0])) { + routeList() + break + } + if (args[0] === 'get' && args.length === 2) { + routeGet(args[1]) + break + } + if (args[0] === 'add' && args.length === 3) { + routeAdd(args[1], args[2]) + } + + if (args[0] === 'delete' && args.length === 3) { + routeDel(args[1], args[2]) + } + break + case '-V': + case '--version': + printVersion() + break + default: + break + doHelp() +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cc5907e --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "bash-ip", + "version": "0.0.1", + "description": "类似于linux下的简单版的ip命令工具", + "main": "index.js", + "bin": { + "ip": "index.js" + }, + "keywords": ["ip", "iproute2"], + "dependencies": { + "es.shim": "0.0.2", + "chalk": "^2.4.1" + }, + "engines": { + "node": ">=8.0.0" + }, + "os": ["darwin"], + "author": "yutent", + "license": "MIT" +}