在实际问题处理中,我们不可避免地会遇到一些文件或其他操作的批量处理。在 Linux 系统中,我们可以利用 shell 脚本去完成这些重复的事情,减少一些不必要的操作。window 的 bat同理。
有兴趣的同学可以看一下前一篇
一、系统脚本足够强大吗?
在处理实际问题时,系统脚本命令能够完全满足我们的需求吗?以 bat 为例,在处理一些简单的逻辑时,dos能满足我们的需求,但是一旦需要进行一些复杂的逻辑判断,甚至是计算时,dos就无能为力了。
例如:一个文件夹内有 1.txt - 100.txt 100个文件,我希望对这些文件重命名为 001.txt - 100.txt。这个时候 dos 能处理吗?
二、node + dos
在 node.js 中,我们可以进行复杂的逻辑运算,同时我们也可以利用 node.js 执行 dos 命令。即通过两者之间的结合,合理利用两者之间的优势,我们可以完成更高难度的操作。
三、看一个场景
在日常开发中,test 目录下,存在若干个子项目,当前开发项目长期依赖 test 下的子项目,因此希望能有一个脚本支持更新编译这些子项目。
难点:这些子项目开发过程中的编译均为 npm run dev
,当该命令会进行一个 watch 操作。
四、设计思路
- 更新所有子模块代码到最新的 develop 分支
- 依次编译各自子模块,等子模块相关操作完成后,结束该操作,执行下一个模块编译
五、相关代码
run.bat1
2@for %%f in (a b c d) do @call update_module.bat %%f
node ./update/update.js
update_module.bat1
2
3
4
5@cd %1
git reset --hard
@git checkout develop
@git pull origin develop
@cd ../
update.js1
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
// https://github.com/pkrumins/node-tree-kill
const kill = require('./tree-kill');
const rootPath = __dirname;
getAllModules(rootPath)
.then(list => {
buildModules(list, 90000)
.catch(e => {
console.log(e);
});
})
.catch(e => {
console.log(e);
});
// 编译模块列表
async function buildModules(list, delayTime) {
for (let i = 0; i < list.length; i++) {
await buildModule(list[i], delayTime);
}
}
// 编译指定模块
function buildModule(moduleName, delayTime) {
return new Promise((resolve, reject) => {
const commond = `cd ${moduleName} && npm run dev`;
const child = exec(commond);
console.log('\x1b[36m%s: \x1b[32m%s \x1b[0m %s', '[start]', moduleName, commond);
child.stdout.on('data', data => {
console.log(`> ${data}`);
});
child.stderr.on('data', data => {
console.log('\x1b[31m%s\x1b[0m ', data);
reject(data);
});
child.on('exit', (code, signal) => {
console.log(`child process exit: ${code} ${signal}`);
resolve();
});
setTimeout(() => {
console.log('\x1b[33m[end]\x1b[0m: ', `时间到结束子任务${moduleName}`);
// child.kill(child.pid); // 子进程无限循环时,无法杀死
kill(child.pid);
}, delayTime || 120000);
});
}
// 更新模块信息
function updateModule(moduleName) {
return new Promise((resolve, reject) => {
const commond = `cat update_module.bat ${moduleName}`;
const child = exec(commond);
console.log('\x1b[36m%s: \x1b[32m%s \x1b[0m %s', '[start]', moduleName, commond);
child.stdout.on('data', data => {
console.log(`> ${data}`);
});
child.stderr.on('data', data => {
console.log('\x1b[31m%s\x1b[0m ', data);
reject(data);
});
child.on('exit', (code, signal) => {
console.log(`child process exit: ${code} ${signal}`);
resolve();
});
});
}
/**
* 获取所有需要update的模块
* @return {Array} 模块列表
*/
function getAllModules(root) {
return new Promise((resolve, reject) => {
const dealModules = [];
readdir(root)
.then(files => {
const tasks = [];
files.forEach(file => {
let task = isDirectory(path.resolve(root, file));
tasks.push(task);
task.then(flag => {
if (flag) {
dealModules.push(file);
}
});
});
Promise.all(tasks)
.then(() => {
resolve(dealModules);
})
.catch(e => {
reject(e);
});
})
.catch(e => {
reject(e);
});
});
}
/**
* 读取一个目录下的所有文件或子目录
*/
function readdir(root) {
return new Promise((resolve, reject) => {
fs.readdir(root, (e, files) => {
if (e) {
return reject(e);
}
resolve(files);
});
});
}
/**
* 判断某路径是否为文件夹/目录
*/
function isDirectory(path) {
return new Promise((resolve, reject) => {
fs.stat(path, (e, stat) => {
if (e) {
return reject(e);
}
if (stat.isDirectory()) {
resolve(true);
} else {
resolve(false);
}
});
});
}