一统江湖的大前端(4)shell.js——穿上马甲我照样认识你
>《一统江湖的大前端》系列是自己的前端学习笔记,旨在介绍javascript在非网页开发领域的应用案例和发现各类好玩的js库,不定期更新。如果你对前端的理解还是写写页面绑绑事件,那你真的是有点OUT了,前端能做的事情已经太多了, `手机app开发` , `桌面应用开发` , `用于神经网络人工智能的库` , `页面游戏` , `数据可视化` , 甚至 `嵌入式开发` ,什么火就搞什么,活脱脱一个**蹭热点小能手**。如果你也觉得前端的日常开发有些枯燥,不妨一起来看看前端的另一番模样。
> 前端开发人员的工作内容几乎很少会涉及脚本的编写,建议将shell.js和git的命令行指令综合在一起作为专题学习,集中学习一下常用指令。更详细的参数请参考专门的shell脚本语言资料进行学习。
#### 一.Shell && Shelljs
码农界存在着无数条鄙视链,`linux`使用者对`windows`的鄙视便是其中之一,`cli`使用者对`GUI`用户的嘲讽也是如此,在这样一个讲究**逼格**的时代,如果你的桌面上没有一个小黑窗时不时地从下往上翻滚并抛出一些亮绿色的字符串,你真不好意思跟人打招呼。而`前端`这种天生几乎不用和命令行打交道的物种,自然再一次莫名其妙地处在了鄙视链的末端,没错,是再一次。

`Shell`是`linux`下的脚本语言解析器,拥有丰富且强大的底层操作权限。`Shelljs`就是基于`node`的一层命令封装插件,让前端开发者可以不依赖`linux`也不依赖类似于`cmder`的转换工具,而是直接在我们最熟悉不过的`javascript`代码中编写`shell`命令实现功能。
#### 二.前端开发人员学Shelljs干嘛
`shell`跟`自动化`是强相关的,个人理解其用途主要是两方面:
+ 1.从业务逻辑的需求来看,`shelljs`并不是什么具有非凡意义的插件,它只是对`node`的底层API进行了一些封装,方便我们以类似`shell`的语法去编写代码梳理逻辑,实现一些业务逻辑需求,如果你所在的项目组恰好需要这样的能力,用它会很方便;
+ 2.`cli`相对于`GUI`或许是更快,但它依然是一种重复劳作,有了shelljs和全栈能力,开发者可以将团队中**耗时的重复性常规动作**编写为自动化脚本,并利用前端的天然优势为其配备`GUI`,用页面上的一键点击来替代重复劳作,在紧张的开发节奏中,平均每天为你节约个**30-40**分钟起来走走喝杯水难道不好吗?
> 想要一统江湖,大前端的深度和广度是缺一不可的,你可以说你不精通shell,但不要说自己不懂shell,更不要一脸天真地反问面试官“前端还能搞shell?这么神奇?”他不会觉得你对知识有好奇心,只会觉得你很low,哦不对,是大写的LOW.
#### 三.官方示例(包含注释)
废话说完了,开始学习,拿好小本子,我要开车了。
“`javascript
//引入shelljs
var shell = require(‘shelljs’)
//检查控制台是否以运行`git `开头的命令
if (!shell.which(‘git’)) {
//在控制台输出内容
shell.echo(‘Sorry, this script requires git’);
shell.exit(1);
}
shell.rm(‘-rf’,’out/Release’);//强制递归删除`out/Release目录`
shell.cp(‘-R’,’stuff/’,’out/Release’);//将`stuff/`中所有内容拷贝至`out/Release`目录
shell.cd(‘lib’);//进入`lib`目录
//找出所有的扩展名为js的文件,并遍历进行操作
shell.ls(‘*.js’).forEach(function (file) {
/* 这是第一个难点:sed流编辑器,建议专题学习,-i表示直接作用源文件 */
//将build_version字段替换为’v0.1.2′
shell.sed(‘-i’, ‘BUILD_VERSION’, ‘v0.1.2’, file);
//将包含`REMOVE_THIS_LINE`字符串的行删除
shell.sed(‘-i’, /^.*REMOVE_THIS_LINE.*$/, ”, file);
//将包含`REPLACE_LINE_WITH_MACRO`字符串的行替换为`macro.js`中的内容
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);
}
“`
#### 三.官方示例中涉及的命令解释:
+ **shell.which(command)**
> 在环境变量`PATH`中寻找指定命令的地址,判断该命令是否可执行,返回该命令的绝对地址。
+ **echo**
> 在控制台输出指定内容
+ **exit(code)**
> 以退出码为`code`退出当前进程
+ **rm**
> 删除一个目录中一个或多个*文件*或*目录*,一旦删除,无法恢复。 `常用参数`:
+ -f:强制删除文件;
+ -i:删除之前先询问用户;
+ -r:递归处理目录;
+ -v:显示处理过程;
+ **cp([options,] source_array, dest)**
> 用来将一个或多个源文件或目录复制到指定的文件或目录。 `常用参数`:
+ -f:强制删除文件;
+ -i:删除之前先询问用户;
+ -r:递归处理目录;
+ **cd**
> 切换工作目录至指定的相对路径或绝对路径。`cd..`为返回上一级,`cd-`回到前一目录。
+ **ls**
> 用来显示目标列表。 `常用参数`:
+ -a:显示所有文件;
+ -C:多列显示查询结果;
+ -l:单列长格式显示查询结果(与-C相反);
+ -R:递归处理目录;
+ **sed([options,] search_regex, replacement, file_array**
> 将`file_array`中符合`search_regex`的内容替换为`replacement`,支持正则的捕获组自引用。一次处理一行内容,处理完成后把缓冲区内容送往屏幕,然后处理下一行,循环直至结束。功能丰富且用法较复杂,建议自行百度进行专题学习。
+ -i:直接作用源文件
+ **cat**
> 将一个或多个文件内容读入,指定一个文件时读入该文件,指定多个文件时将内容连接在一起读入。
+ **exec(command,[, options][, callback])**
> 执行所传入的命令
+ async:是否异步执行,默认`false`,传入callback时自动开启
+ slient:不输出信息到console,默认`false`
+ encoding:默认`utf8`
#### 四.文档中其他API概览
+ **chmod**
> 设置文件调用权限
+ **基本语法** :chmod [-cfvR] [–help] [–version] mode file…
+ -c:若文件权限确实被更改,才显示更改动作
+ -f: 权限无法被更改时不显示错误信息
+ -v: 显示权限变更的详细资料
+ -R: 递归,对其目录下所有文件和子文件执行相同操作
+ **mode字段格式** : [ugoa…][[+-=][rwxX]…][,…]
+ u表示该文件拥有者,g表示同一群体者,o表示其他,a表示所有
+ +表示增加权限,-表示取消权限,=表示唯一设定权限
+ r表示可读,w表示可写,x表示可执行,X表示当该文件是个子目录?
+ **find(path[,path…])**
> 寻找路径
+ **grep([options,] regex_filter,file)**
> 从指定文件中抓取符合正则的行
+ -v:翻转正则匹配
+ -l:仅打印符合条件的文件名
+ **head([{‘-n’: