node-实现一个命令行工具

1,992 阅读2分钟

先上效果图

总结一下上篇说的效果:是命令行的方式可执行系统命令可输入参数

接下来一个个需求分步实现

环境:macOS 10.15.2 node 12.16.1

命令行方式

先准备脚本目录
mkdir xlfdcli
cd xlfdcli
npm init
# 不需要后缀
touch x-cli
编辑x-cli
#!/usr/bin/env node
console.log('success')

到这一步,修改一下权限,其实已经可以运行了: 怎么去掉./直接以x-cli执行?

在package.json中添加

添加"bin": {"x-cli": "x-cli"},当然也可以换其他的名字"bin": {"xlfdcli": "x-cli"}

{
  "name": "xlfdcli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "bin": {
    "x-cli": "x-cli"
  },
  "author": "",
  "license": "ISC"
}

npm link

执行npm link后即可在全局使用x-cli,这里执行 npm link 的时候,会有权限的问题,可以使用sudo:

sudo npm link
#npm WARN xlfdcli@1.0.0 No description
#npm WARN xlfdcli@1.0.0 No repository field.

#up to date in 2.007s
#found 0 vulnerabilities

#/usr/local/bin/x-cli -> /usr/local/lib/node_modules/xlfdcli/x-cli
#/usr/local/lib/node_modules/xlfdcli -> /Users/johndiamond/Documents/workspace/xlfdcli
x-cli
#success

执行系统命令

安装npm i --save shelljs后有三种方式可以执行:

#!/usr/bin/env node

// ### child_process ###
const { exec } = require('child_process')
exec(`echo 参数是:${process.argv[2]}`, (error, stdout, stderr) => {
    if (error) throw error
})

// ### shelljs ###
const shelljs = require('shelljs')
shelljs.exec(`echo 参数:${process.argv[2]}`)

// ### shelljs/global ###
require('shelljs/global')
const dirName = 'global'
mkdir(dirName)
cd(dirName)
touch('creatbyglobal.txt')
echo('done')

输入参数

安装npm i --save yargs

#!/usr/bin/env node
const { argv } = require('yargs')
console.log(argv)
console.log(argv.type)

结合 一峰老师的命令行教程已经知道上面三个需求怎么实现了

命令行内容

基本思路:手动在ngin.conf中定义两类标识对应上面三个功能;根据脚本参数替换标识重启使nginx配置生效

标识nginx.conf

又结合上面的知识,就很好实现我们上篇最后的需求了(小部分未完善)

因为代码比较简单,就不注释了

#!/usr/bin/env node

const Fs = require('fs')
const { argv } = require('yargs')
    .usage('x-cli [options]')
    .example('x-cli -t location -u /privacy')
    .help('h')
    .alias('h', 'help')
    .epilog('creat by xlfd')
    .option('t', {
        alias: 'type',
        demand: true,
        type: 'string',
        describe: '配置类型 server 和 location'
    })
    .option('u', {
        alias: 'uri',
        demand: true,
        type: 'string',
        describe: 'location的uri'
    })
    .option('a', {
        alias: 'alias',
        type: 'string',
        describe: 'location的别名'
    })
    .option('r', {
        alias: 'root',
        type: 'string',
        describe: 'server的根目录root'
    })
    .option('i', {
        alias: 'index',
        type: 'string',
        describe: 'location的资源index'
    })
    .option('p', {
        alias: 'port',
        type: 'number',
        describe: 'server的端口'
    })

const nginxPath = '/usr/local/nginx/sbin'
const _type = argv.t
const _uri = argv.u
const _alias = argv.a
const _index = argv.i
const _port = argv.p

if (_type === 'location') {
    addLocation({
        path: './nginx.conf',
        port: _port,
        index: _index,
        uri: _uri,
        alias: _alias
    } )
} else {
    addServer({
        path: './nginx.conf',
        port: _port,
        index: _index,
        uri: _uri,
        alias: _alias
    } )
}

function addLocation ({path, port = 80, index, uri, alias} = {}) {
    const markStr = `#jenkins-CI-newLocation${port}`
    const locationStr = `location ${uri} {
        ${alias ? `alias ${alias}`: '#'};
        index ${index ? index : 'index.html'} index.html;
    }
    ${markStr}`
    readFile(path, markStr, locationStr, 'location')
}

function addServer ({path, port = 80, index, uri, alias} = {}) {
    const serverMarkStr = '#jenkins-CI-newServer'
    const locationMarkStr = `#jenkins-CI-newLocation${port}`
    const locationStr = `location ${uri} {
        ${alias ? `alias ${alias}`: '#'};
        index ${index ? index : 'index.html'} index.html;
    }
    ${locationMarkStr}`
    const serverStr = `server {
        listen ${port};
        server_name localhost;
        ${locationStr}
    }
    ${serverMarkStr}`
    readFile(path, serverMarkStr, serverStr, 'server')
}

function readFile (path, source, target, type = 'location') {
    Fs.readFile(path, (error, data) => {
        if (error) throw error
        let confStr = data.toString()
        // 替换标识
        confStr = confStr.replace(source, target)
        Fs.writeFile(path, confStr, (err) => {
            if(err) throw err
            console.log(`${type === 'location' ? 'location' : 'server'} add success`)
            // 重启nginx
            reload(nginxPath)
        })
    })
}

function reload (path) {
    const shelljs = require('shelljs')
    shelljs.exec(`cd ${path}`)
    shelljs.exec('./nginx -s reload')
    console.log('nginx reloaded')
}

这个命令行可以嵌入到jenkins-CI 脚本里去实际使用,不过实际使用的时候要注意路径