![e053761e2a096c8b0606d8c1090e60dd.png](https://i-blog.csdnimg.cn/blog_migrate/2d81596e373778c7c2caa189957a64f0.jpeg)
本文主要讲一些工具开发的前置知识,很基础但很重要!文章的最后有本文涉及到的Github代码仓库。
node.js 是基于 Google V8 引擎的 JavaScript 运行时环境。是前端迈向全栈的重要技术,也是目前最简单的全栈语言。它能做的事情非常多:跨平台、前端、后端、以及各种工具的开发,覆盖的场景也非常广。
工具开发可以说是最常见的使用场景。工程化过程中:dev、build、test、ci、cd等各个环节,从脚手架到各种预编译工具以及构建工具等等可以说是无处不在。
但工作中我们往往是去使用别人开发好的工具,自己很少去实现。
下图参考:
【全文】狼叔:如何正确的学习Node.js - CNode技术社区cnodejs.org![15b3bda4f92fb5286d89328e966d215f.png](https://i-blog.csdnimg.cn/blog_migrate/e9fb460b5d94c1e62d76ebca2f7a6f5b.png)
![59e7f5d1fe7f7a2c0cbef1a89d91428d.png](https://i-blog.csdnimg.cn/blog_migrate/c5623e89417e984ffe6ef1b8726f8214.jpeg)
目录
- 需要了解的基础知识
- shell script介绍
- 环境变量 PATH 到底是什么?
- #!/usr/bin/env node 是什么?
- commander.js
一、需要了解的基础知识
以下基础需要有一定了解,当然并不需要很深。
- 操作系统 os
- 命令行 command
- 文件系统 fs
- 网络通信 net
二、shell script 介绍
shell对于工具开发非常重要,需要有比较全面的了解。
shell相对于我们平时使用的编程语言来说要简单很多。只是很少使用,不熟悉而已。
我们先将shell切换为bash。其实就是因为 shell有很多种类,而bash是大多数系统默认的shell。Linux chmod命令 | 菜鸟教程www.runoob.com
脚本切换
$ chsh -s /bin/bash
1.shell
参考:
Shell 教程 | 菜鸟教程www.runoob.com Shell 是用来解决什么问题的?www.zhihu.com- Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
- Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
- Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
总结:一种可控制操作系统的程序,将用户输入与kernel沟通,控制硬件工作。
![5aa56a002c7997b7c93b1210bf8fad5f.png](https://i-blog.csdnimg.cn/blog_migrate/616189f2220c3a949f3d1cd882df9223.jpeg)
![8ac6e4cd4d3dadcd171214a07fece035.png](https://i-blog.csdnimg.cn/blog_migrate/2c51577f203f7f447bea131b83a10eba.png)
2.bash、zsh与iterm2
bash
、zsh
:都是shell,而 zsh 兼容bash,功能更强大
item2
:和mac的terminal 类似,是终端模拟器
![78919587aa327d18d85e9a985789aa3e.png](https://i-blog.csdnimg.cn/blog_migrate/7244ecda4603cfe61d088ad77fd28e06.png)
常见的 shell 脚本解释器
/bin/sh (已经被 /bin/bash 所取代)
/bin/bash (就是 Linux 默认的 shell)
/bin/ksh (Kornshell 由 AT&T Bell lab. 发展出来的,兼容于 bash)
/bin/tcsh (整合 C Shell ,提供更多的功能)
/bin/csh (已经被 /bin/tcsh 所取代)
/bin/zsh (基于 ksh 发展出来的,功能更强大的 shell)
iterm2是一个替代终端和iTerm的后继项目(命令行应用)
官网:
首页-iterm2-Mac OS 终端工具www.iterm2.cn3. shell 的实用功能
a.他能记忆使用过的命令:.bash_history
b.命令与文件补全功能: ([tab] 按键的好处)
c.命令别名配置功能: (alias)
d.工作控制、前景背景控制: (job control, foreground, background)
e.程序化脚本: (shell scripts)
f.通配符: (Wildcard)
4.shell script
shell script 是利用 shell 的功能所写的一个程序,这个程序是使用纯文本,将一些 shell 的语法与命令(含外部命令)写在里面, 搭配正规表示法、管线命令与数据流重导向等功能,以达到我们所想要的处理目的。
总结:一个程序,纯文本文件,可利用 shell 与相关工具指令来进行一些操作,无需编译。
如下,在根目录下面创建一个文件夹 shell_demo,然后再循环创建10个子文件。
#!/bin/bash
cd ~
mkdir shell_demo
cd shell_demo
for ((i=0; i<10; i++)); do
touch test_$i.txt
done
# 【给文件添加执行权限: chmod 777 demo.sh】 【然后执行: ./demo.sh】
# 第1行: 指定脚本解释器,这里是用/bin/sh做解释器的
# 第2行: 切换到当前用户的home目录
# 第3行: 创建一个目录 shell_demo
# 第4行: 切换到shell_tut目录
# 第5行: 循环条件,一共循环10次
# 第6行: 创建一个test_0…9.txt文件
# 第7行: 循环体结束
5.#!/bin/bash 的作用
#
不是注释!/bin/bash
,#!
读作 hashbang,告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序。
- 使用bash语法
- 运行时能够加载bash的相关配置文件
- 如果不写操作系统会自动判断使用的bash (猜测,不一定准确,所以尽量写上)
6.shell 执行方式
[1] 工作目录执行
# 进入目录后执行(也叫相对路径)
$ ./demo.sh
[2] 绝对路径执行
$ `pwd`/demo.sh
[3] sh执行
# 指的是用脚本对应的sh或bash来接着脚本执行
$ sh demo.sh
[4] shell环境执行
# 指的是在当前的shell环境中执行,可以使用 . /demo.sh 接脚本 或 source 脚本
$ source demo.sh
# 或
$ . demo.sh
7.执行方式的差异
直接执行会在子程序的 bash 中执行,变量无法回传,exit 值可以;
[1] [2][3] 脚本中创建的所有变量或动作都将会结束而不会回到主程序中
![deeb8e83bc005d6c5c19761cf5a18ce0.png](https://i-blog.csdnimg.cn/blog_migrate/683ce3688826d1472ea79c6df02b7241.jpeg)
![e6ebab13147f0005503e2ab5e77ace78.png](https://i-blog.csdnimg.cn/blog_migrate/52433f9c7911919629482fbf3e859762.png)
source 会在父程 序中执行
[4] 将变量加入到主程序bash当中
![e9a10e71c966faed78a505ae093861d3.png](https://i-blog.csdnimg.cn/blog_migrate/3c63463b34b4bbfcab87f36d5ed44003.jpeg)
![0ca77caf7bc7b6f1ffbad3d84d9b0618.png](https://i-blog.csdnimg.cn/blog_migrate/b9edde198523c2b74ad7e217b30f35e2.png)
这就是【直接执行】与用【source命令执行】的区别,前者只作用于子进程本身,后者则作用于整个父进程。
因此:如要想不注销系统,并让全局配置文件生效,则必须用source命令。
# 在配置 path 环境变量的时候 经常会用到。例如:
$ source /etc/profile
三、环境变量 PATH 到底是什么?
对于习惯用 Mac 的同学,经常会遇到配置各种环境变量文件,很多时候都是在网上搜索,然后粘贴。其实并没有很好的理解其中的原理,导致每次遇到环境问题都需要花很长时间去处理。理解 path 最重要的就是理解各种配置文件的执行顺序。
环境变量的本质就是变量,它的服务对象是 shell 程序,它分为局部环境变量和全局环境变量,分别有不同的作用域和自然生命周期。
以Mac os 为例(环境变量 加载顺序为[1 -> 2 -> 3 -> 4 -> 5 -> 6])
【1.2 系统级别,系统启动后就会加载】
1 /etc/profile
2 /etc/paths
【3.4.5 用户级别的环境变量】
3 ~/.bash_profile
【如果.bash_profile 存在4.5忽略不执行,否则以此类推读取后面的文件】
4 ~/.bash_login
5 ~/.profile
【.bashrc 没有上述规则,它是bash shell 打开的时候载入】
6 ~/.bashrc
【全局文件】下面的几个文件设置是全局的,修改时需要root权限
1)/etc/paths (全局建议修改这个文件)
编辑 paths,将环境变量添加到 paths文件中 ,一行一个路径
2)/etc/profile (建议不修改这个文件)
全局(公有)配置,不管是哪个用户,登录时都会读取该文件
3)/etc/bashrc (一般在这个文件中添加系统级环境变量)
全局(公有)配置,bash shell 执行时,不管是何种方式,都会读取此文件
【用户文件】单个用户设置,修改时不需要root权限
1)~/.bash_profile (任意一个文件中添加用户级环境变量)
(注:Linux 里面是 .bashrc 而 Mac 是 .bash_profile)
若 bash shell 是以 login 方式执行时,才会读取此文件
该文件仅仅执行一次!默认情况下,他设置一些环境变量
2)~/.bashrc 同上
如果想立刻生效,则可执行下面的语句:
$ source 相应的文件
一般环境变量更改后,重启后生效
四、#!/usr/bin/env node
如果定义为 #!/usr/bin/env node 内容用js 写,程序会不会运行?【会】
我们来创建一个可执行脚本 hello,参考:
npm scripts 使用指南www.ruanyifeng.com![ae1a529218257c897bc80bd029358b2a.png](https://i-blog.csdnimg.cn/blog_migrate/66f773c9b89c14079fe624e5e40a98d7.jpeg)
![cc909b8e7846f50aabac182f549a9063.png](https://i-blog.csdnimg.cn/blog_migrate/bc97ca35aed17b46885181eaa8daf5db.png)
创建 hello 文件
#!/usr/bin/env node
含义:用node作为解释器,它的查找路径是 /user/bin/env
#!/usr/bin/env node
console.log('hello world');
操作
// 1.执行
node hello
// 输出 hello world
// 2.执行 ./hello
// 无法执行,需要赋执行权限
chmod 755 hello
// 3.再次执行 ./hello
// 输出 hello world
// 4.如果我们想 直接运行 hello 就执行
// 需要创建添加 package.json 配置
package.json 配置
{
"name": "hello",
"bin": {
"hello": "hello"
}
}
添加软链接
$ npm link
// 5.再次执行 hello
// 输出 hello world
此时我们的系统中就有了一个可执行 shell 脚本 hello
以上整个过程中介绍了shell 脚本以及一些概念,最后我们用node 实现了一个 非常简单的shell脚本。
五、commander
nodejs 开发出的工具,使用场景大部分都是 命令行工具(Command-Line Interface)形式,也就是 cli。比如: vue-cli。
所以需要先了解一个 commander 工具包,其提供了用户命令行输入和参数解析强大功能,让nodejs 开发命令行工具更加容易。
process
介绍 commander 前,先了解一下 process。
process
对象是一个全局变量,它提供有关当前 Node.js 进程的信息并对其进行控制。 作为一个全局变量,它始终可供 Node.js 应用程序使用,无需使用 require()
。我们熟知的参数有:
process.env
:包含用户环境的对象process.argv
:返回一个数组,其中包含当启动 Node.js 进程时传入的命令行参数。 第一个元素是process.execPath
。 第二个元素将是正在执行的 JavaScript 文件的路径。 其余元素将是任何其他命令行参数。
官方解释:
process | Node.js API 文档nodejs.cnso
process.argv 的规律很简单。我们在写命令行程序时,只需要对process.argv
这个数组的第三个元素及其之后的参数进行解析即可。
如果不嫌麻烦,完全可以写出很多判断分支来做。但是现在我们有更好的方法, 就是使用 commander.js。
安装
$ npm install commander
官方例子
test.js
var program = require('commander')
program
.version('0.1.0')
.option('-C, --chdir <path>', 'change the working directory')
.option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
.option('-T, --no-tests', 'ignore test hook');
program
.command('setup [env]')
.description('run setup commands for all envs')
.option("-s, --setup_mode [mode]", "Which setup mode to use")
.action(function(env, options){
var mode = options.setup_mode || "normal";
env = env || 'all';
console.log('setup for %s env(s) with %s mode', env, mode);
})
program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option("-e, --exec_mode <mode>", "Which exec mode to use")
.action(function(cmd, options){
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
}).on('--help', function() {
console.log('');
console.log('Examples:');
console.log('');
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
})
program
.command('*')
.action(function(env){
console.log('deploying "%s"', env);
})
program.parse(process.argv)
运行
![2f8c5a27e686b68a6ecdc861b5c44620.png](https://i-blog.csdnimg.cn/blog_migrate/6489049eaf88396d53a059438ec8732e.jpeg)
核心API
version
// 用法:
.version('x.y.z')
// 用于设置命令程序的版本号,
option
// 用法:
.option('-C, --chdir <path>', 'change the working directory')
// 第一个参数是选项定义,分为短定义和长定义。用|,,,连接。
// 参数可以用<>或者[]修饰,前者意为必须参数,后者意为可选参数。
// 第二个参数为选项描述
// 第三个参数为选项参数默认值,可选。
command
// 用法:
.command('init <path>', 'description')
// command的 用法稍微复杂,原则上他可以接受三个参数,第一个为命令定义,第二个命令描述,第三个为命令辅助修饰对象。
// 第一个参数中可以使用<>或者[]修饰命令参数
// 第二个参数可选。
// 当没有第二个参数时,commander.js将返回Command对象,若有第二个参数,将返回原型对象。
// 当带有第二个参数,并且没有显示调用action(fn)时,则将会使用子命令模式。
// 所谓子命令模式即,./pm,./pm-install,./pm-search等。这些子命令跟主命令在不同的文件中。
// 第三个参数一般不用,它可以设置是否显示的使用子命令模式。
description
// 用法:
.description('command description')
// 命令的描述信息
action
// 用法:
.action(fn)
// 用于设置命令执行的相关回调。
// fn可以接受命令的参数为函数形参,顺序与command()中定义的顺序一致。
parse
// 用法:
program.parse(process.argv)
// 此 api 一般是最后调用,用于解析 process.argv。
outputHelp
// 用法:
program.outputHelp()
// 一般用于未录入参数时自动打印帮助信息。
我们应该对脚本有了一定的理解,此时再去用 node 实现 shell 脚本应该是非常容易了。接下来我们用它去实现一个简单的翻译工具。运行效果:
代码地址:github/node-shell
![3276739df23ea7edbdb96d67cd377bf5.png](https://i-blog.csdnimg.cn/blog_migrate/6f9cc8c0051dd030579d508c23de91f7.jpeg)
![8de5f46cbc477301075223c86e77c658.png](https://i-blog.csdnimg.cn/blog_migrate/1f476dc5e63e78b1de0f477446157707.jpeg)
总结:本文主要讲了一些前置知识,接下来我们会基于这些基础知识去实现一个前端工程化中的脚手架工具!