全局命令背后的原理

22,289 阅读2分钟

背景

最近在刷掘金的时候,刷到了腾讯IVWEB团队的 Feflow 工作流项目,出于好奇去略读了一下源码,里面主要包含项目初始化、开发、构建、检查、发布五个方向。所有功能都集成于 fef <command>,例如:

  1. 初始化项目
fef init
  1. 安装脚手架
# 安装脚手架
fef install @feflow/generator-example
# 卸载脚手架
fef uninstall @feflow/generator-example
# 列出本地安装的脚手架
fef list
  1. 开发
fef dev
  1. 构建
fef build
  1. 检查
fef lint [folder]
  1. 发布部署
fef deploy

看上面的命令都是基于 fef <command>,是不是眼前一亮?

为了不跑题,我们今天主要聊聊主角 fef 命令是如何注册到全局命令中的,后面我会分章节对 feflow 源码做一次分析,并且讲解如何创建一个企业级的工作流。

第三方的可执行命令

在我们项目的node_modules下都有一个.bin文件夹,下面都是当前项目可执行的命令文件,下面我通过创建一个简单的项目带大家了解一下:

  1. 创建项目目录
mkdir commands && cd commands
  1. 初始化项目
npm init -y
  1. 安装依赖
npm i webpack -D

执行完上述操作后, 我们会发现node_modules/.bin下面已经有了可执行命令 webpack,如下图:

image.png

这个时候我们就可以在项目根目录下执行 webpack 命令了:

node_modules/.bin/webpack

image.png

不过这样写好像有点别扭,我们还可以通过 npx 来执行命令,npm@5.2 版本开始增加了npx命令,如下:

npx webpack

npx 的原理很简单,就是运行的时候,会到node_modules/.bin路径和环境变量$PATH里面,检查命令是否存在。

image.png

我们最常用的还是写在scripts里面,然后通过npm run <command>执行,如下

{
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack"
    }
}
npm run dev

npm 脚本的原理也非常简单。每当执行npm run,就会自动新建一个 Shell,在这个 Shell 里面执行指定的脚本命令。因此,只要是 Shell(一般是 Bash)可以运行的命令,就可以写在 npm 脚本里面。

比较特别的是,npm run新建的这个 Shell,会将当前目录的node_modules/.bin子目录加入PATH变量,执行结束后,再将PATH变量恢复原样。

这意味着,当前目录的node_modules/.bin子目录里面的所有脚本,都可以直接用脚本名调用,而不必加上路径。

image.png

环境变量 PATH

上面我们在讨论 npm run 的时候提到了环境变量 PATH,下面我们会详细说明一下

什么是环境变量 PATH?

我们回顾一下,系统是怎么知道我们可执行文件是写在哪里的?我们猜一下,我一开始是觉得是有个列表维护了所有可执行文件的地址。为了证明猜想,我们查看一下

env

image.png

如何添加一个目录到环境变量 PATH 中?

  1. 命令行添加
export PATH=~/Desktop/command/bin:$PATH

注意当关闭终端后,声明的环境变量会被清除。那有没有什么方法可以把自定义的环境变量注册进去呢,看下面第二点

  1. 修改配置文件

    2.1 打开编辑窗口

    # 如果使用的终端是ITerm
    open ~/.zshrc
    # 如果使用的终端是bash
    open ~/.bash_profile
    

    2.2 打开编辑窗口后添加如下内容:

    export PATH=~/Desktop/command/bin:$PATH
    

    2.3 保存更新应用

    # ITerm 终端保存
    source ~/.zshrc
    # bash 终端保存
    source ~/.bash_profile
    

创建一个简单的命令

了解了环境变量的作用和配置后,我们手动配置一条命令,加深理解

  1. 创建项目目录
mkdir command && cd command
  1. 创建并编辑可执行文件
vim ccy
#!/usr/bin/env node
console.log('这是一条测试命令')
  1. 将可执行命令挂载到环境变量上,这里我们采用一次性挂载
export PATH=~/Desktop/command:$PATH
  1. 执行命令
ccy

image.png

我们发现执行失败了, 输出permission denied: ccy, 说明拒绝访问 ~/Desktop/command 下的 ccy 可执行文件。

我们需要使用linux下的 chmod, 这是控制用户对文件权限的命令。具体使用就自行百度吧~,我们执行下面代码

chmod u+x ~/Desktop/command/ccy

image.png

通过以上讲解不知道你理解可执行命令背后的原理了吗?欢迎在评论里留言