不管在传统的企业级系统维护仍是在互联网运维中,Shell脚本的编写与维护经常必不可少,在系统管理员或开发人员工做中占比重比较大的一部分。 Shell脚本的严格语法格式对于通常的运维人员来讲,经常会在一不留神下而抓狂或查找半天才发现是由于多了或少了一个空格或某语包括号不匹配而致使的错 误,不但大大的浪费了脚本维护人员的工做时间,还可能影响到工程进度甚至项目的发布里程碑等。固然,对于非纯Geek来讲,最重要的仍是影响心情,特别是 对于一些较复杂的脚本需求,更是必须当心谨慎,所以愈来愈多的开发人员必须借助于Python、Perl、Ruby等相关的脚本语言来实现,可是常因为平 台的特性或者语言的限制,对系统级的命令调用或者异常处理有限制,最终解决起来并非十分优雅。 NodeJS的出现或许会给这些开发人员带来一些新的选择。 html
NodeJS从诞生起发展很是迅速,社区活动很是活跃,目前扩展模块达到1500多个,而且天天都有不一样的模块提交。它是构建在JavaScript引擎V8之上的JavaScript环境,它采用基于单线程的异步事件驱动I/O模型,具备很是高的性能, 同时可以支持多种平台。日前国外的不少大的软件或互联网公司如Microsoft,ebay,yahoo等都在使用NodeJS,国内的网易,淘宝,新浪 等互联网企业也有不少分享和成功的线上案例应用。言归正传,但愿下文的内容能给不熟悉或不喜欢nix平台Shell脚本开发或WIN平台下的批处理编写的 工程师带来一些帮助,为简单起见,本文采用Nix平台为示例,WIN平台的用户请参考自行修改或与做者联系。 前端
首先,我认可Shell脚本中系统命令再加上sed,awk等瑞士军刀在一块儿工做已经至关强大,若是你想了解NodeJS的强大之处和如何结合Shell产生强大的工做效率,而且还能具备很好的灵活性,那就让咱们继续旅程吧: node
先看一段简单的采用Shell 脚本执行一段命令获得其执行时间的脚本diffa.sh: git
#!/bin/bash START=$(date +%s) # prepare things du -m /home > /tmp/output # done END=$(date +%s) DIFF=$(( $END - $START )) echo "化了$DIFF 秒搞定" chmod +x diff.sh sh diff.sh
执行上面的脚本后,结果以下: github
化了0 秒搞定用户首次执行通常会耗时几秒,屡次执行的结果可能会在0-1秒之间随机显示。由于du的输出重定向,整个脚本的执行时间很是短,而且脚本中采用的是 秒数级别的范围,若是须要获得这个脚本的准确执行时间只能用纳秒来进行,并在Shell作除法运算,把脚本修改一下diffb.sh。 chrome
START=$(date +%s%N) du -m /home/ >/tmp/output END=$(date +%s%N) DIFF=$(($END - $START)) SUM=`expr $DIFF / 1000000` echo "化了$SUM MS搞定"
执行一下上面的diffb 脚本就能够获得运行的结果了,须要提醒的是上面的脚本中各表达式的格式都是即定的,若是开发人员不当心多了一个空格或少了一空格,都将致使脚本错误。下面 采用NodeJS来试试看,首上下载与安装NodeJS环境,过程很是简单,具体请参考官方网站或直接apt-get 之类的操做。编写以下diffc.js脚本: apache
#!/usr/bin/env node var util = require('util'), spawn = require('child_process').spawn, ls = spawn('du', ['-m', '/home/']); var start = +new Date(); ls.stdout.on('data', function (data) { //console.log('stdout: ' + data); }); ls.stderr.on('data', function (data) { console.log('stderr: ' + data); }); ls.on('exit', function (code) { var end = +new Date(); console.log(end - start); });
注:上面require中引用的都是系统内置模块,spawn的格式为spawn(command, [args], [options]),其余请参阅官方文档。 npm
一样,chmod +x 对脚本赋予执行权限,执行脚本./diff.js,结果以下: 编程
1113
上面示例中显示的时间直接是毫秒级别,代码格式没有严格的限制,流程的控制也会更加灵活,特别是在异常状况下,能够根据用户的需求处理更小的细节。 固然,我认可这个示例需求有些诡异,可是作这样的比较,并非说要两者一决高下,只是换一种前端攻城师喜欢的方法去实现一些系统运维需求。在这里 NodeJS脚本自己也是依赖于系统Shell的强大基础之上。 api
以上示例能够看到,在Shell环境中,NodeJS内置模块实现经常使用的功能是即方便又灵活,Linux Shell环境中比较强大的功能之一就是支持输入输出重定向功能,用符号<和>来表示。0、1和2分别表示标准输入、标准输出和标准错误信息 输出,用来指定须要重定向的标准输入或输出,好比 2>error.log表示将错误信息输出到文件err.log中。相似的,NodeJS中能够直接采用超复杂的命令来搞定,通常对于咱们这些非系 统管理员有必定的难度,下面引入强大点的模块procstreams,它能够实现输出流重定向等功能,首先用户须要执行npm install procstreams安装模块,编写示例以下wc.js:
#!/usr/bin/env node var p = require('procstreams'); p('cat app.log').pipe('wc -l').data(function (stdout, stderr) { console.log(stdout); });
wc.js脚本代码是借助于Shell命令实现统计app.log的行数,至关于Shell环境中的cat app.log | wc -l功能,输出的结果能够再根据须要再进行灵活处理,另外它还支持then、and和or等操做,相似Shell脚本中的;、&& 和||操做。在实现复杂或交互的功能时,甚至能够彻底采用交互的方式进行操做输入。
另外,用户执行脚本的时候还须要处理复杂一些的参数对应,node-optimist 及 isaacs's nopt 之类的模块能够很是简单的帮助攻城师实现这样的功能,如实现根据用户的输入的参数执行须要的系统命令,并能够作相关的逻辑处理的opt.js:
#!/usr/bin/env node var util = require('util'), spawn = require('child_process').spawn; var argv = require('optimist').argv; var cmd = argv.cmd; var args = argv.args var option = argv.opt var ls = spawn(cmd, [args, option]); ls.stdout.on('data', function (data) { if (!data || !! data) console.log(' i believe it'); }); ls.stderr.on('data', function (data) { console.log('It\'s a miracle!'); }); ls.on('exit', function (code) { console.log('it.justHappened();'); });
用户使用以下对应格式执行代码:./opt.js --cmd=ls --args=/m --opt=/home,而后只须要在代码相关处添加对应的逻辑代码,把注意力放在业务层,采用js的流程控制实现业务逻辑的分离。
在企业线上或系统运维中,常须要对一些进程进行监控和报警,以便通知相关系统管理人员,以下Shell脚本 agenta.sh实现了对tomcat6进程监控,若是不存在自动重启。
#!/bin/sh pid=`ps aux| grep "tomcat6" | grep -v grep | sed -n '1P' | awk '{print $2}'` if [ -z $pid ]; then echo "begin restart,please waiting..." sudo /etc/init.d/tomcat6 restart exit 1 else echo -e "exist ,don't need restart" fi
脚本编写人员在通过一番努力与折腾后,完成了代码编写与调试工做,而后须要经过系统的crontab功能添加如0-59/2 * * * * sh agent.sh的定时任务,若是系统管理员把crontab的权限给禁用了,那就须要获得系统管理员的帮助了。下面使用Nodejs来实现一样的功能, 先假设读者对grep、sed和awk等经常使用命令的使用有大概了解,代码以下agentb.js:
var p = require('procstreams'); var exec = require('child_process').exec; setInterval(function () { exec("ps aux| grep 'tomcat6' | grep -v grep | sed -n '1P' | awk '{print $2}'", function (err, output) { if (err) throw err; if (output.length > 0) console.log('exist,don\'t need restart'); else exec('sudo /etc/init.d/tomcat6 restart', function (err2, out2) {}); }) }, 1000 * 60 * 2);
示例代码中setInterval的函数的做用经过设置一个回调函数和间隔执行时间来实现定时监控。运行代码后,一样能够实现进程监控的功能,也许 你会说上面的Shell命令仍是不少的。由于你以为直接使用Shell脚本会更简单,但是若是你经历过为空格或配置之类的调试过程,或者需求更加复杂时, 采用NodeJS会让你以为很是轻松。更重要的是,编写脚本后,在执行脚本时你能够直接经过chrome debug 工具设置断点与单步调试,或者在chrome 浏览器上进行图形化调试等操做,具体请参考node-inspector。如今,agentb.js代码中的Shell命令仍是太长了太复杂,调试起来也 不太方便,使用procstreams作一下简化,实现代码agentc.js以下:
var p = require('procstreams'); setInterval(function () { p("ps aux").pipe('grep tomcat6').pipe('grep -v grep').pipe('sed -n 1P').pipe("awk $2") .data(function (stdout, stderr) { if (stderr) throw stderr; if (stdout.length > 0) { console.log('exist,don\'t need restart'); } else { console.log('restart,waiting...'); p('sudo /etc/init.d/tomcat6 restart', function (stdout, stderror) { console.log(stdout); }); } }); }, 1000 * 60 * 2);
agentc代码中经过pipe操做能够实现对每一个步骤的输入进行详细的跟踪与调试,可是脚本中仍是须要对系统的不少内置命令有大概的了解,须要对 操做系统的相关功能或语法格式比较熟悉,使用起来仍是有点不习惯。攻城师都喜欢编程时能控制住本身把握的,或者在使用简单的命令的状况下,就能实现须要的 功能,再次简化代码后获得agentd.js
var p = require('procstreams'); var serviceName = 'tomcat6'; var interval = 1000 * 60 * 2; setInterval(function () { p("ps aux").pipe('grep ' + serviceName).data(function (stdout, stderr) { if ( !! stdout && stdout.indexOf(serviceName) == 0) { console.log('exist,don\'t need restart'); } else { console.log('restart,waiting...'); p('sudo /etc/init.d/tomcat6 restart', function (stdout, stderror) {}); } }); }, interval);
在通过此次修改以后,对系统命令的掌握程度要求明显更低了,题外话,用户对系统命令了解的越详细越好,但若是使用简单即美的指导去实现一样的需求,何乐而不为。代码中serviceName 和interval 参数能够经过node-optimist模块动态给定,这样就能够实现一份代码监控多个进程,而且不须要系统管理员的帮助去添加定时任务的操做。固然,但愿这样操做不会影响系统功能或在权限范围内。
尽管Linux的Shell环境编程很是的强大,可是编写或调试Shell脚本时常使人抓狂不已,也没有很好的图形化调试工具。固然脚本较复杂时, 尤为在需求跨平台时,脚本改动比常较大,日前,开发人员须要根据平台的不一样,准备多套脚本代码,如tomcat,apache等,若是采用简单Shell 和NodeJS结合编程,或许只须要把平台相关的命令提取出来,只需较少改动就能实现跨平台,能够大大提升工做效率与减小浪费攻城师的时间。我的认为,采 用两者结合的方式具备如下优势:
随着NodeJS不断发展和成熟,国内外厂商愈来愈多的成功案例与分享,在企业级和互联网系统应用开发和维护中具备更广阔的前景。