版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/stalendp/article/details/79077859
// 用jar的打包方式创建aar文件
var fs = require('fs');
var path = require('path');
const util = require('util');
const exec = util.promisify(require('child_process').exec);
async function jar(fn) {
var cmd = 'jar cvf ' + fn + ".aar -C " + fn + " " + fn + "/*";
// const { stdout, stderr } = await exec(cmd);
// console.log('stdout:', stdout);
// console.log('stderr:', stderr);
console.log(cmd);
}
var dirs = p => fs.readdirSync(p).filter(f=> fs.statSync(path.join(p,f)).isDirectory());
(function() {
var files = dirs('.');
files.forEach(function(f){
if(fs.statSync(f).isDirectory()) {
var fn = path.basename(f);
if(fn!="HeroesArena" && fn!="lib_source") {
// create jar
jar(fn);
}
}
});
})();
// 生成Eclipse工程。
// 运行环境,安卓node.js, 并安装node插件:
// npm install unzip-stream --save
var fs = require('fs');
var path = require('path');
var unzip = require('unzip-stream');
var dirs = p => fs.readdirSync(p).filter(f=> fs.statSync(path.join(p,f)).isDirectory());
var basedir = path.join("..", "Assets", "Plugins", "Android");
// 一些要处理的aar文件
var aars = ["appcompat-v7-24.1.1", "cardview-v7-24.1.1", "design-24.1.1", "firebase-common-10.2.1",
"firebase-iid-10.2.1", "firebase-messaging-10.2.1", "installreferrer-1.0", "play-services-basement-10.2.1",
"play-services-tasks-10.2.1", "recyclerview-v7-24.1.1", "support-v4-24.1.1", "support-vector-drawable-24.1.1"
];
aars.forEach(function(aarName) {
var aarFile = path.join(basedir, aarName + ".aar");
var aarPath = path.join("..", "AndroidBuild", aarName);
// 解压aar文件到AndroidBuild目录下
var stream = fs.createReadStream(aarFile).pipe(unzip.Extract({ path: aarPath }));
stream.on("close", function(){
var libPath = path.join(aarPath, "libs");
var libFile = path.join(aarPath, "classes.jar");
if(!fs.existsSync(libPath)) {
fs.mkdirSync(libPath);
}
// 处理classes.jar文件,改个名字,并移动到libs目录下
if(fs.existsSync(libFile)) {
fs.renameSync(libFile, path.join(libPath, aarName + ".jar"));
}
// 为了编译通过,需要创建src目录
var srcPath = path.join(aarPath, "src");
if(!fs.existsSync(srcPath)) {
fs.mkdirSync(srcPath);
}
console.log(aarName);
});
});
(function() {
var abp = path.join("..", "AndroidBuild");
var files = dirs(abp);
files.forEach(function(f){
var ff = path.join(abp, f);
if(fs.statSync(ff).isDirectory()) {
var fn = path.basename(ff);
if(fn=="HeroesArena" || fn=="lib_source") {
var srcPath = path.join(ff, "src");
if(!fs.existsSync(srcPath)) {
fs.mkdirSync(srcPath);
}
}
}
});
})();
一个日志解析工具
var fs = require('fs');
var path = require('path');
var DID = 149496940;
var lr = require('readline').createInterface({ // 一行一行读取文件
input: require('fs').createReadStream('sample.log')
});
//https://stackoverflow.com/questions/30452263/is-there-a-mechanism-to-loop-x-times-in-es6-ecmascript-6-without-mutable-varia
const times = n => f => { // 循环函数定义,参考下面的调用
let iter = i => {
if (i === n) return
f (i)
iter (i + 1)
}
return iter (0)
}
// 一些正则表达式的使用方法
//182.61.120.11 ha.mobaonline.com - [27/Jan/2018:23:48:30 -0800] "GET /pixel.jpg?client=moba&os=2&type=onWindowFocusChanged_1&is50mClient=1&hd=0&version=1.2.9.184&appVersion=1.2.9&obbVersion=128&channel=googleplay&uuid=ffffffff8b74d89eddf7ae8e0033c587&user_id=14660192&newbie=0&sn=0&f=unity HTTP/1.0" 200 119 "-" "Dalvik/2.1.0 (Linux; U; Android 6.0.1; US699 Build/MMB29M)" "-" "0.229" "-" "-" "-" "182.61.33.150" "182.61.33.150, 113.134.222.207, 113.134.222.207, 182.61.33.150, 182.61.120.11"
var reg = /type=([^&]+).*?user_id=(\d+)/i;
var regTime = /ReportOnlineTime_(.*)/i;
var regTimeAll = /(ReportOnlineTime|onWindowFocusChanged_0|OnlineTime|RoomConnected|ProxyConnect|StorageFull|CErrorDLCode|CErrorCheckDstHash|CErrorDL4Times|CErrorDLOpenFile)_.*/i;
var User = function(_id){
const CNT = 10;
var id = _id;
var duration = 0;
var queue = new Array(CNT);
var replaced = [];
var count = 0;
var fixQueue = function() {
var tmp = [];
var start = count > CNT ? count - CNT : 0;
var n = count > CNT ? 10 : count;
times(n) (i => { // 循环函数的调用
var d = queue[start%CNT];
tmp.push(d.replace(regTimeAll, "$1"));
start++;
});
queue = tmp;
};
return {
get ID() { // getter函数
return id;
},
parse: function(d) {
var m = regTime.exec(d);
if(m) {
var tt = parseInt(m[1]);
if(duration < tt) {
duration = tt;
}
}
queue[count%CNT] = d;
count++;
},
analysis: function(us) {
fixQueue();
queue.forEach(e => {
replaced.push(us.getIdx(e));
});
},
print: function() {
console.log(id + "->" + replaced.join(","));
}
};
};
var cc = 0;
var Users = function() {
var us = [];
var dict = {}; // Dictionary的用法
var dCnt = 0;
return {
parse: function(uid, data) {
var u = us[uid];
if(u == null) {
cc++;
u = new User(uid);
us[uid] = u;
}
u.parse(data);
},
getIdx: function(elm) {
var rt = dict[elm];
if(rt==null) {
rt = dCnt;
dict[elm] = dCnt;
dCnt++;
}
return rt;
},
print: function() {
us.forEach(u => {
u.analysis(this);
});
us.forEach(u => {
u.print();
});
console.log(dict);
}
};
};
var DebugUser = function(_uid) {
var uid = _uid;
var ds = [];
return {
parse : (id, d) => {
if(id == uid) {
ds.push(d);
}
},
print : v => {
console.log("================== " + uid + " ==============");
ds.forEach(elm => {
// console.log(elm);
});
}
};
};
//======================= MAIN Start =========================
var du = new DebugUser(DID);
var us = new Users();
var t0 = new Date().getTime();
lr.on('line', line => {
var m = reg.exec(line);
if(m) {
var id = m[2];
var dd = m[1];
us.parse(id, dd);
du.parse(id, dd);
}
}).on('close', v => {
us.print();
du.print();
var t1 = new Date().getTime();
console.log("Finished by " + ((t1 - t0)/1000.0) + " seconds.") // 计算运行时间
});
//======================= MAIN End =========================
处理iOS的崩溃日志的工具,原理参考 Understanding and Analyzing Application Crash Reports
其实就是atos工具的使用,目前添加了对文件的处理。输入是 heroesarena.app.dSYM和crash.log,输出是crash_parsed.log
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var stream = require('stream');
// https://github.com/abbr/deasync
// npm install deasync
var deasync = require('deasync');
var cp = require('child_process');
const es = require('event-stream');
var cmdPre = "atos -arch arm64 -o heroesarena.app.dSYM/Contents/Resources/DWARF/heroesarena -l";
var reg = /heroesarena\s+(0x[^\s]+)\s+(0x[^\s]+)/i;
var lr = require('readline').createInterface({
input: fs.createReadStream('crash.log')
});
var exec = deasync(cp.exec);
//0 heroesarena 0x0000000100ed3514 0x100040000 + 15283476
var reg2 = /^\d+\s+heroesarena\s+((0x[^\s]+)\s+(0x[^\s]+).*)$/i;
var reg3 = /(0x[^\s]+\s+0x[^\s]+.*$)/i;
var parse = function(dict) { // convert the file
fs.createReadStream('crash.log')
.pipe(es.split())
.pipe(es.mapSync(l => {
var m = reg2.exec(l);
if(m) {
var t1 = dict[m[3]];
if(t1!=null) {
var t2 = t1[m[2]];
if(t2!=null) {
l = l.replace(reg3, t2);
}
}
}
return l;
}))
.pipe(es.join('\n'))
.pipe(fs.createWriteStream('crash_parsed.log'))
.on('close', () => {
console.log('Done!');
});
};
var dict = [];
lr.on('line', line => {
var m = reg.exec(line);
if(m) {
var addr1 = m[2];
var offsets = dict[addr1];
if(offsets == null) {
offsets = [];
dict[addr1] = offsets;
}
var addr2 = m[1];
if(offsets[addr2] == null) {
offsets[addr2] = addr2;
}
}
}).on('close', v => {
Object.keys(dict).forEach(adr1 => {
var adr2s = dict[adr1];
var keys = Object.keys(adr2s).map(x => x);
var rst = exec(`${cmdPre} ${adr1} ${keys.join(' ')}`); // 这里的写法,类似于stringFormat
var lines = rst.split(/\r?\n/);
for(var i=0; i<keys.length; i++) {
adr2s[keys[i]] = lines[i];
}
parse(dict);
});
});
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var stream = require('stream');
var crypto = require('crypto');
var projDir = "D:\\_Moba\\Working\\1.7"; // 设置工程文件夹!!
var baseDir = path.join(projDir, "\\Assets\\BuildOnlyAssets");
var okFiles = {};
var okFile2 = {};
var md5 = str => crypto.createHash('md5').update(str).digest('hex'); // MD5相关算法
var tmpDirs = [];
var reg = /([^\.]+)\..+$/i;
var reg2 = /(?:[^\\/]+[\\/])*(([^\.]+)(\..+)?)$/gi;
// 遍历文件夹,并计算MD5
var myIter = function(cur, dirs) {
// console.log(cur);
var full = path.join(baseDir, cur);
fs.readdirSync(full).forEach(f => {
if(!f.startsWith(".")) {
var tmp = path.join(cur, f);
if(fs.statSync(path.join(baseDir, tmp)).isDirectory()) {
dirs.push(tmp);
} else { // 计算MD5
if(!f.endsWith(".meta") ) {
var fn = `SuI9_hero_project_2014/${tmp.toLowerCase().replace(/\\/gi, "/").replace(reg, "$1")}.assets`;
var m = md5(fn); // MD5的用法
okFiles[m] = tmp;
okFile2[tmp.replace(reg2, "$1")] = m;
}
}
}
});
}
tmpDirs.push(".");
while(tmpDirs.length > 0) { // 处理所有的文件
myIter(tmpDirs.shift(), tmpDirs); // queue的用法
}
// 把文件大小,显示成合适的大小
var fmtSize = function(size) { // 格式化文件大小的显示
var tn = ['B', 'K', 'M', 'G', 'T'];
var t = 0;
while(size > 1024) {
size /= 1024;
t++;
}
return `${size.toFixed(2)}${tn[t]}`; // float保留两位小数
}
var jsonfile = "fileList4.json";
if(fs.existsSync(jsonfile)) { // 打印一些下载包大小
var lr = require('readline').createInterface({ // 一行一行读取文件
input: require('fs').createReadStream(jsonfile)
});
var reg = /"f":"(\d+)".+"o":(\d)/i;
var size1=0, size2=0;
lr.on("line", line => {
var m = reg.exec(line);
if(m) {
if(m[2]==1) {
size2 += parseInt(m[1]); // 把字符串转化为int
} else {
size1 += parseInt(m[1]);
}
} else {
// console.log(line);
}
}).on("close", line => {
console.log(`required_Size: ${fmtSize(size1)}, optional_Size: ${fmtSize(size2)}`);
});
}
console.log("输入查询的文件:");
var rl = require('readline').createInterface({
input:process.stdin,
output:process.stdout
});
rl.on('line', function(line){
if(line.length<=0)
return;
if(line.startsWith(">>")) {
var reg = />>\s*([^\.]+)(?:\.(.*))?$/gi;
var fn = line.toLowerCase().replace(/\\/gi, "/");
var m = (reg).exec(fn);
if(m[2]) { // 有后缀
if(/(?:bnk)|(?:wen)|(?:ddx)|(?:ddv)|(?:txt)/gi.test(m[2])) {
fn = fn.replace(reg, "SuI9_hero_project_2014/$1.$2")
} else {
fn = fn.replace(reg, "SuI9_hero_project_2014/$1.assets");
}
} else {
fn = fn.replace(reg, "SuI9_hero_project_2014/$1.assets");
}
var m = md5(fn);
console.log(`${fn} => ${m}`);
} else {
var dict = okFiles;
var reg = /(?:[^\\/]+[\\/])*(([^\.]+)(\..+)?)$/gi;
var m = reg.exec(line);
if(!m)
return;
if(!m[3] || m[3] == ".ddx") {
dict = okFiles;
line = m[2];
} else {
dict = okFile2;
line = m[1];
}
// console.log(nn);
var realName = dict[line];
if(realName) {
console.log(realName);
} else {
console.log("Not found! ");
}
}
console.log();
});
rl.on('close', function() {
console.log('bye bye');
process.exit(0);
});
// 一些测试
// var lr = require('readline').createInterface({
// input: fs.createReadStream('patch.json')
// });
// var reg = /{".":"(.\/[^\"]+)"/i;
// var dict = [];
// lr.on('line', line => { //
// var m = reg.exec(line);
// if(m) {
// var fn = m[1];
// var realName = okFiles[fn];
// if(!realName) {
// console.log("not found ==> " + fn);
// } else {
// console.log(realName);
// }
// }
// }).on('close', v => {
// console.log("Done!!");
// });
通过adb shell列出文件内容
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
function exec(cmd, parameter, callback) {
var proc = require('child_process').exec(cmd);
var content = "";
proc.stdout.setEncoding('utf8');
proc.stdout.on('data', function (chunk) {
content += chunk;
});
proc.stdout.on('end', function () {
callback(content);
});
proc.stdin.write(parameter);
}
exec("adb shell", "cd /sdcard/Android/data/com.ucool.heroesarena/patch6 && find . -type f \\( -name '*.ddx' -o -name '*.bnk' \\) -print0 | xargs -0 ls -al && exit \r\n", str => {
console.log(str);
});
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var proc = require('child_process');
var baseDir = "D:\\_Moba\\Working\\client_1.5";
baseDir = path.join(baseDir, "Assets\\BuildOnlyAssets");
// Promise的用法参考 http://exploringjs.com/es6/ch_promises.html
var exec = function(cmd) {
return new Promise((resolve, reject) => {
var p = proc.exec(cmd, {maxBuffer: 100 * 1024 * 1024}, (err, stdout, stderr) => {
if(err) {
reject(err);
} else {
resolve(stdout);
}
});
});
}
var jsonName = "fileList4.json";
var delJson = function() {
if(fs.existsSync(jsonName)) {
fs.unlinkSync(jsonName);
}
};
// {"a":"0/013952985da37eff54db7669b280106b.ddx","b":"f1698cba18bc707705f48d96659e5bc7","c":"13213","d":"1.0.84.1077","e":"1",
// "f":"6968","g":"ad3283ca38a64955083e8bf7746f6305","h":"1","t":1,"o":0},
var FileInfo = function(str) {
var info;
try {
info = JSON.parse(str.replace(/([^}]+}),?/, "$1"));
} catch(e) {
}
var isHit = false;
var fullname = null;
return {
get IsValid() { return info && info.a && info.b; },
get filename() { return info.a; },
get hash() { return info.b; },
get size() { return info.c; },
get dlSubFolder() { return info.d; },
get dlSuccess() { return info.e; },
get downloadSize() { return info.f; },
get downloadHash() { return info.g; },
get autoUncompress() { return info.h; },
get optional() { return info.o; },
get fileType() { return info.t; },
get IsHint() { return isHit ;},
markHit : function () { isHit = true; },
get Fullname() {
if(fullname == null) {
if(okFiles) {
var reg = /.\/([^\.]+)\.ddx/;
if(reg.test(this.filename)) {
fullname = okFiles[this.filename.replace(reg, "$1")];
}
}
if(fullname==null) {
fullname = "-";
}
}
return fullname;
},
get desc() {
return `${this.filename} : ${this.Fullname} : ${this.optional ? "optional" : "required"}`;
}
};
}
var dict = {};
var okFiles = {};
var md5 = str => require('crypto').createHash('md5').update(str).digest('hex'); // MD5相关算法
var tmpDirs = [];
tmpDirs.push(".");
// 遍历文件夹,并计算MD5
var myIter = function(cur) {
// console.log(cur);
var full = path.join(baseDir, cur);
fs.readdirSync(full).forEach(f => {
if(!f.startsWith(".")) {
var tmp = path.join(cur, f);
if(fs.statSync(path.join(baseDir, tmp)).isDirectory()) {
tmpDirs.push(tmp);
} else { // 计算MD5
if(!f.endsWith(".meta") ) {
var fn = `SuI9_hero_project_2014/${tmp.toLowerCase().replace(/\\/gi, "/").replace(/([^\.]+)\..+$/, "$1")}.assets`;
var m = md5(fn); // MD5的用法
okFiles[m] = tmp;
}
}
}
});
}
var readDirs = new Promise((resolve, reject) => { // 读取本地文件,并形成字典
while(tmpDirs.length > 0) { // 处理所有的文件
myIter(tmpDirs.shift()); // queue的用法
}
resolve();
});
readDirs.then(r=> {
exec('adb pull /sdcard/Android/data/com.ucool.heroesarena/fileList4.json') // 拉取fileList4.json
}).then(r => { // 解析文件
return new Promise((resolve, reject) => {
if(!fs.existsSync(jsonName)) {
return reject();
}
var lr = require('readline').createInterface({
input: fs.createReadStream(jsonName)
});
lr.on('line', line => {
var info = new FileInfo(line);
if(info.IsValid) {
dict[info.filename] = info;
}
}).on('close', v => {
resolve();
});
});
}).then(r => { // 列出手机上相应目录下的文件名
return exec('adb shell "cd /sdcard/Android/data/com.ucool.heroesarena/patch6 && find . -type f \\( -name \"*.ddx\" -o -name \"*.bnk\" \\) -print0 | xargs -0 ls -al"');
}).then(r => { // 尝试对比,是否有文件缺失
return new Promise((resolve, reject) => {
//-rw-rw---- 1 u0_a260 sdcard_rw 85794 2018-03-21 14:45 ./c/cf9da209844daa8db78dd4140e000c34.ddx
var reg = /(?:\S+\s+){4}(\d+)\s+(?:(?:\S+\s+){2})\.\/([^\r\n]+)/g;
// 全局文本搜索 https://stackoverflow.com/questions/1222045/how-to-loop-all-the-elements-that-match-the-regex
var m;
while((m = reg.exec(r)) !== null) {
var f = dict[m[2]];
if(f) {
if(m[2].startsWith("Audio")) {
// console.log(m[2]);
}
f.markHit();
} else {
console.log(`${f} is not exists in fileList4.json!`);
}
}
resolve();
});
}).then(r => { // 打印结果
Object.keys(dict).forEach(key => {
var f = dict[key];
if(!f.IsHint) {
console.log(`NOT FOUND:: ${f.desc}`);
}
});
console.log("============== Run Finished!!==================");
// delJson();
}) .catch(r => {
console.log(`Error: ${r}`);
// delJson();
});
一个词典:
var fs = require('fs');
var lr = require('readline').createInterface({
input: fs.createReadStream('words1M.csv')
});
var pageSize = 30;
var pattern = process.argv[2].replace(/(\*)/g, "\.$1").replace(/(\?)/g, "\.$1");
var page = parseInt(process.argv[3]) || 0;
var skip = page * pageSize;
re = new RegExp(`^${pattern}$`); // /^.*acro.*$/i;
var count = 0, lc = 0;
lr.on('line', line => {
lc++;
if(re.test(line)) {
count++;
if(count > skip && (count - skip) <= pageSize) {
console.log(`${("" + count).padEnd(5)}${line.padEnd(20)}${("" + (lc/10000).toFixed(4)).padStart(8)}`);
}
}
}).on('close', v => {
console.log('---------------------------------------');
console.log(`${pattern} page: ${page}/${parseInt(count/pageSize)} total: ${count}`);
});
一个单词查询工具
var fs = require('fs');
const readline = require('readline');
var lr = readline.createInterface({
input: fs.createReadStream('words1M.csv')
});
var dict = {}
var init = function(total) {
var acc = total / 10, acc_ = 0;
process.stdout.write('Loading');
return new Promise((resolve, reject) => {
var lc = 0;
lr.on('line', line => {
lc++;
if (lc > total)
return;
var w = line;
var next = dict;
for (var j = 0; j < w.length; j++) {
var c = w.charAt(j);
if (next[c] == null) {
next[c] = {};
}
next = next[c];
}
if (next) {
if (next['@'] == null) {
next['@'] = lc;
} else {
// console.warn("has number!");
}
}
if(++acc_ > acc) {
acc_ = 0;
process.stdout.write('.');
}
}).on('close', v => {
resolve();
});
});
};
var search = w => {
var next = dict;
for (var i = 0; i < w.length; i++) {
var c = w.charAt(i);
if (next[c] != null) {
next = next[c];
} else {
next = null;
break;
}
}
return next && next['@'] || -1;
}
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
var query = function() {
return new Promise((resolve, reject) => {
rl.question('> ', w => {
if(w.length == 0) {
resolve();
return;
}
var ss = w.split(',');
var rt = [];
for(var s of ss) {
s = s.trim().toLowerCase();
var idx = search(s);
if(idx > 0) {
rt.push([s, idx]);
}
}
rt.sort((lhs, rhs) => lhs[1] - rhs[1]);
var l1 = "", l2 = "", ls = "";
for(var s of rt) {
var wl = s[0].length;
var ll = 15;
while(ll<=wl) {
ll += 15;
}
l1 += s[0].padEnd(ll);
ls += "".padEnd(ll, '-');
l2 += s[1].toString().padEnd(ll);
}
resolve(`${l2}\r\n${ls}\r\n${l1}\r\n\r\n`);
});
});
};
(async function() {
await init(800000);
while(true) {
var result = await query();
if(result) {
console.log(`${result}`);
}
}
})();
1. 介绍Java面向对象,很好的书籍:
《The principles of Object-Oriented Javascript》 by Nicholas C. Zakas
2. 介绍Node.js很好的书籍(目前感觉Stream和pipe方面的介绍非常ok):
《Node.js Design Patterns》by Mario Casciaro