NodeJS运行Shell以及解析Nginx配置

Node运行Shell命令

child_process(原生)

Nodejs下引入模块child_process实现调用shell

调用的两种方式

1
2
child_process.exec(command[, options][, callback])
child_process.execFile(file[, args][, options][, callback])

Nodejs中通过 exec执行shell脚本,并打印查询到的信息

1
2
3
4
5
var child = require('child_process');

child.exec('ls', function(err, sto) {
console.log(sto);//sto才是真正的输出,要不要打印到控制台,由你自己啊
})

执行文件

1
2
const exec = require('child_process').execSync
exec('bash ./shell/shell1.sh hello')

对应的shell文件

1
2
3
#!/bin/bash
# This is our first script.
echo "$1" > log.txt

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const util = require('util');
const path = require('path');
const child_process = require('child_process');
// 调用util.promisify方法,返回一个promise,如const { stdout, stderr } = await exec('rm -rf build')
const exec = util.promisify(child_process.exec);
const appPath = path.join(__dirname, 'app');

const runClean = async function () {
// cwd指定子进程的当前工作目录 这里的rm -rf build为删除指定目录下的一个文件夹
await exec(`rm -rf build`, {
cwd: appPath
});
await exec(`rm -rf test`, {
cwd: appPath
});
}
runClean();

注意

util.promisify是在node.js 8.x版本中新增的一个工具,用于将老式的Error first callback转换为Promise对象,让老项目改造变得更为轻松。

shelljs(三方)(推荐)

shelljs是j基于nodeAPI的一个扩展,要引入插件:(npm地址);

它比原生的child_process的兼容性更好,使用更灵活,这个插件的使用率很高。

安装

1
npm install shelljs --save

child_process同样的调用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var shell = require('shelljs');
var version = shell.exec('node --version', {
silent: true
}).stdout;
console.info("version", version);

var child = shell.exec('pwd', {
async: true
});

child.stdout.on('data', function (data) {
console.log('data:', data);
});

shell.exec('pwd', function (code, stdout, stderr) {
console.log('Exit code:', code);
console.log('Program output:', stdout);
console.log('Program stderr:', stderr);
});

这个插件不仅可以调用exec执行shell命令,也封装了一些快捷操作指令,具体使用文档请参考github地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var shell = require('shelljs');
// cat 返回文件内容
const mdres = shell.cat(' * .md')

// pwd 获取当前目录
const res = shell.pwd();

// 查找文件
shell.find('src', 'lib');
shell.find(['src', 'lib']); // same as above
shell.find('.').filter(function (file) {
return file.match(/.js$/);
});

// 创建目录
shell.mkdir('-p', '/tmp/a / b / c / d', '/tmp/e / f / g');
shell.mkdir('-p', ['/tmp/a / b / c / d', '/tmp/e / f / g']);

// 复制
shell.cp('file1', 'dir1');
shell.cp('-R', 'path/to/dir/', '~/newCopy/');
shell.cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp');
shell.cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above

官方示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var shell = require('shelljs');
if (!shell.which('git')) {
shell.echo('Sorry, this script requires git');
shell.exit(1);
}

// Copy files to release dir
shell.rm('-rf', 'out/Release');
shell.cp('-R', 'stuff/', 'out/Release');

// Replace macros in each .js file
shell.cd('lib');
shell.ls('*.js').forEach(function (file) {
shell.sed('-i', 'BUILD_VERSION', 'v0.1.2', file);
shell.sed('-i', /^.*REMOVE_THIS_LINE.*$/, '', file);
shell.sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, shell.cat('macro.js'), file);
});
shell.cd('..');

// Run external tool synchronously
if (shell.exec('git commit -am "Auto-commit"').code !== 0) {
shell.echo('Error: Git commit failed');
shell.exit(1);
}

simple-git(GIT)

执行shell脚本操作git,其实对于复杂的git命令语句,写起来还是很不方便,最后介绍一个专为git设计的插件:simple-git(npm地址)

  1. 在项目中引入插件后,调用simple-git/promise可执行异步git操作,方便结合async/await使用
  2. 它封装并支持了很多git的方法,比如clone、commit、status、pull等等,将cmd命令和参数,传入即可
  3. 甚至可以用git.raw(),解析前端输入的git命令

安装

1
npm install simple-git

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const simpleGit = require('simple-git/promise');
const path = require('path');

async function gitinit() {
const projectPath = path.join(__dirname);
const cmd = "init";
const args = "";
const git = simpleGit(projectPath);

try {
const res = await git[cmd](args);
console.info("res:", res);
} catch (e) {
console.error('执行 simple-git 命令时发生错误', {
projectPath,
cmd,
args
}, e);
throw e;
}
}

gitinit();

总结

这里总结了几种基于node的方式:

  1. child_process 原生nodeAPI,需根据需要选型
  2. shelljs Node的一个扩展插件、兼容性好,推荐使用
  3. simple-git 专为git命令打造的插件,轻量好用

Node查看服务器配置

NodeJS获取系统信息

http://nodejs.cn/api/os.html

操作系统

1
2
3
4
5
6
7
8
9
10
11
12
13
const os = require('os');
let platform = os.platform();
let name_obj = {
darwin: "MacOS",
win32: "Windows",
linux: "Linux",
};

// 系统类型
let platform_name = name_obj[platform] || platform;

// 版本
let release = os.release();

返回值有

  • darwin
  • freebsd
  • linux
  • sunos
  • win32

CPU及负载

1
2
3
4
5
6
7
const os = require('os');

var cpus = os.cpus();
console.log('CPU:' + cpus.length + '核 ' + cpus[0].speed + 'mHz');

var loadavg = os.loadavg();
console.log(loadavg);

os.loadavg() 方法返回一个数组,包含 1、5 和 15 分钟平均负载。

平均负载是系统活动的测量,由操作系统计算得出,表达为一个分数。 一般来说,平均负载应该理想地比系统的逻辑 CPU 的数目要少。

平均负载是 UNIX 相关的概念,在 Windows 平台上没有对应的概念。 在 Windows 上,其返回值总是 [0, 0, 0]

对于单核心的CPU来说

系统负荷为0,意味着大桥上一辆车也没有

系统负荷为0.5,意味着大桥一半的路段有车。

系统负荷为1.0,意味着大桥的所有路段都有车,但任然可以顺次通行

系统负荷为1.7,除了桥满之外,在桥的入口处还有70%的车辆在等待

对于多核来说,我们可以用负载/核心数量来运算

内存

1
2
3
4
const os = require('os');
console.log('剩余内存(M):' + parseInt(os.freemem() / 1024 / 1024));
console.log('总内存(M):' + parseInt(os.totalmem() / 1024 / 1024));
console.log('CPU 架构:' + os.arch());

网卡信息

1
2
const os = require('os');
console.log('网卡:', os.networkInterfaces());

分配的网络地址的对象上可用的属性包括:

  • address分配的 IPv4 或 IPv6 地址。
  • netmask IPv4 或 IPv6 的子网掩码。
  • family IPv4IPv6
  • mac 网络接口的 MAC 地址。
  • internal 如果网络接口是不可远程访问的环回接口或类似接口,则为 true,否则为 false
  • scopeid 数值型的 IPv6 作用域 ID(仅当 familyIPv6 时指定)。
  • cidr 以 CIDR 表示法分配的带有路由前缀的 IPv4 或 IPv6 地址。如果 netmask 无效,则此属性会被设为 null

硬盘

添加依赖

1
npm i diskinfo

查看硬盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var diskinfo = require('diskinfo');
//获得所有磁盘空间
diskinfo.getDrives(function (err, aDrives) {
//遍历所有磁盘信息
for (var i = 0; i < aDrives.length; i++) {
//盘符号
var mounted = 'mounted ' + aDrives[i].mounted;
//总量
var total = 'total ' + (aDrives[i].blocks / 1024 / 1024 / 1024).toFixed(1) + "gb";
//已使用
var used = 'used ' + (aDrives[i].used / 1024 / 1024 / 1024).toFixed(1) + "gb";
//可用
var available = 'available ' + (aDrives[i].available / 1024 / 1024 / 1024).toFixed(1) + "gb";
//使用率
var capacity = 'capacity ' + aDrives[i].capacity;
console.log(mounted + "\r\n" + total + "\r\n" + used + "\r\n" + available + "\r\n" + capacity);
console.log("==================================================================");
}
});

这个支持Win和Linux,不支持MacOS

Node解析Nginx配置

安装依赖

1
npm install nginx-conf --save

说明文档:https://www.npmjs.com/package/nginx-conf

生成静态项目配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
const NginxConfFile = require('nginx-conf').NginxConfFile;
var fs = require('fs');

//配置项
let conf_path = `${__dirname}/nginx/`;
let server_name = "test.psvmc.cn";
let cert_pem = "/etc/nginx/cert/xhkjedu.pem";
let cert_key = "/etc/nginx/cert/xhkjedu.key";
let root_path = "/data/web_front/psvmc.com";
let useSSL = true;

const filename = `${conf_path}${server_name}.conf`;

fs.access(filename, fs.constants.F_OK, (err) => {
if (!err) {
fs.unlinkSync(filename);
}
fs.writeFile(filename, '', {
'flag': 'a'
}, function (err) {
if (err) {
throw err;
}
readconf();
});
});

function readconf() {
NginxConfFile.create(filename, function (err, conf) {
if (err || !conf) {
console.log(err);
return;
}

// server
conf.nginx._add('server');

conf.nginx.server[0]._add('listen', '80');
conf.nginx.server[0].listen[0]._comments.push('http');
conf.nginx.server[0]._add('server_name', server_name);
conf.nginx.server[0]._add('client_max_body_size', '200m');

if (useSSL) {
// SSL
conf.nginx.server[0]._add('listen', '443');
conf.nginx.server[0].listen[1]._comments.push('https');
conf.nginx.server[0]._add('ssl', 'on');
conf.nginx.server[0]._add('ssl_certificate', cert_pem);
conf.nginx.server[0]._add('ssl_certificate_key', cert_key);
conf.nginx.server[0]._add('ssl_session_timeout', '5m');
conf.nginx.server[0]._add('ssl_ciphers', 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4');
conf.nginx.server[0]._add('ssl_protocols', 'TLSv1 TLSv1.1 TLSv1.2');
conf.nginx.server[0]._add('ssl_prefer_server_ciphers', 'on');
}


// location
conf.nginx.server[0]._add('location', '/');
conf.nginx.server[0].location[0]._comments.push('location');
conf.nginx.server[0].location[0]._add('root', root_path);
conf.nginx.server[0].location[0]._add('index', 'index.html');

console.info("server_name", conf.nginx.server[0].server_name[0]._value);
});
}

生成反向代理配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
const NginxConfFile = require('nginx-conf').NginxConfFile;
var fs = require('fs');

//配置项
let conf_path = `${__dirname}/nginx/`;
let server_name = "test.psvmc.cn";
let upstream_name = server_name.split(".").join("_");
let server_arr = ['110.110.110.110:8080', '120.120.120.120:8080'];
let cert_pem = "/etc/nginx/cert/xhkjedu.pem";
let cert_key = "/etc/nginx/cert/xhkjedu.key";
let useSSL = true;

const filename = `${conf_path}${server_name}.conf`;

fs.access(filename, fs.constants.F_OK, (err) => {
if (!err) {
fs.unlinkSync(filename);
}
fs.writeFile(filename, '', {
'flag': 'a'
}, function (err) {
if (err) {
throw err;
}
readconf();
});
});

function readconf() {
NginxConfFile.create(filename, function (err, conf) {
if (err || !conf) {
console.log(err);
return;
}

// upstream
conf.nginx._add('upstream', upstream_name);
for (const server_item of server_arr) {
conf.nginx.upstream[0]._add('server', server_item);
}

// server
conf.nginx._add('server');

conf.nginx.server[0]._add('listen', '80');
conf.nginx.server[0].listen[0]._comments.push('http');
conf.nginx.server[0]._add('server_name', server_name);
conf.nginx.server[0]._add('client_max_body_size', '200m');

if (useSSL) {
// SSL
conf.nginx.server[0]._add('listen', '443');
conf.nginx.server[0].listen[1]._comments.push('https');
conf.nginx.server[0]._add('ssl', 'on');
conf.nginx.server[0]._add('ssl_certificate', cert_pem);
conf.nginx.server[0]._add('ssl_certificate_key', cert_key);
conf.nginx.server[0]._add('ssl_session_timeout', '5m');
conf.nginx.server[0]._add('ssl_ciphers', 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4');
conf.nginx.server[0]._add('ssl_protocols', 'TLSv1 TLSv1.1 TLSv1.2');
conf.nginx.server[0]._add('ssl_prefer_server_ciphers', 'on');
}


// location
conf.nginx.server[0]._add('location', '/');
conf.nginx.server[0].location[0]._comments.push('location');
conf.nginx.server[0].location[0]._add('proxy_pass', 'http://' + upstream_name + '/');
conf.nginx.server[0].location[0]._add('proxy_cookie_path', '/ /');
conf.nginx.server[0].location[0]._add('proxy_redirect', '/ /');
conf.nginx.server[0].location[0]._add('proxy_set_header', 'Host $host');
conf.nginx.server[0].location[0]._add('proxy_set_header', 'X-Real-IP $remote_addr');
conf.nginx.server[0].location[0]._add('proxy_set_header', 'X-Forwarded-For $proxy_add_x_forwarded_for');
conf.nginx.server[0].location[0]._add('client_max_body_size', '200 m');
conf.nginx.server[0].location[0]._add('client_body_buffer_size', '128k');
conf.nginx.server[0].location[0]._add('proxy_connect_timeout', '300s');
conf.nginx.server[0].location[0]._add('proxy_send_timeout', '300s');
conf.nginx.server[0].location[0]._add('proxy_read_timeout', '300s');
conf.nginx.server[0].location[0]._add('proxy_busy_buffers_size', '64k');
conf.nginx.server[0].location[0]._add('proxy_temp_file_write_size', '64k');
conf.nginx.server[0].location[0]._add('proxy_buffer_size', '64k');
conf.nginx.server[0].location[0]._add('proxy_buffers', '8 64k');
conf.nginx.server[0].location[0]._add('fastcgi_buffer_size', '128k');
conf.nginx.server[0].location[0]._add('fastcgi_buffers', '4 128k');
conf.nginx.server[0].location[0]._add('send_timeout', '60');
console.info("server_name", conf.nginx.server[0].server_name[0]._value);
});
}

注意

所有添加的属性都要以数组的方式来获取

比如 conf.nginx.server[0].server_name[0]._value

修改配置

1
2
3
4
5
6
7
8
// 当配置改变时不写到磁盘中
conf.die('/etc/nginx.conf');
// 将内存中的配置写到另一个文件中
conf.live('/etc/nginx.conf.bak');
// 修改内存中的值
conf.nginx.events.connections[0]._value = 2000; //change remains local, not in /etc/nginx.conf
// 强行将内存中的配置刷到磁盘中
conf.flush();

MacOS 上安装timeout

1
brew install coreutils

使用:

1
gtimeout

还可以加上别名:

1
alias timeout=gtimeout