符号连接(Symbolic)、package.json的bin属性与Shebang

以全局方式安装的npm包,如gulpwebpack等为何能够像mkdircopy这样的shell命令(或程序)同样,在任何文件夹均可以经过命令行调用?跟环境变量又有什么关系?什么是符号连接?javascript

在解释这些问题前,先了解下linux上文件执行相关内容,会更容易理解些java

◎ Linux文件执行的方式

最开始接触shell时,通常会说起shell脚本文件的不一样执行方式。先建立有一条简单命令的shell脚本文件hello.shnode

echo "hello world"
复制代码
  • 一种方式是,直接运行shell解释器,把脚本名做为解释器命令的参数
/bin/sh hello.sh 
# or /bin/zsh hello.sh or zsh hello.sh
hello world
复制代码

其中/bin/sh/bin/zsh 为系统支持的不shell类型,直接使用zsh、sh也是等效的,由于shell会根据环境变量($PATH)路径找到相应的/bin/sh/bin/zshlinux

  • 另外一种方式,如今hello.sh作一点小改变
#! /bin/zsh
echo "hello world"
复制代码

改好以后,须要先让hello.sh文件具备执行权限,由于会直接使用hello.sh执行,不然会报permission denied的错误(即没有执行权限)。拥有执行权限后,就能够这样执行了webpack

>> ./hello.sh   # ro /path/to/hello.sh
hello world
复制代码

实际上,如今依然可使用/bin/sh hello.sh 的形式来执行。问题是,为何加了 #! /bin/zsh 后能够./hello.sh 这样执行呢?
#! /bin/zsh这行有个术语,叫Shebang(or Hashbang),是shell脚本的标准起始行,#! 后面是指定解释器的绝对路径。它由shell程序解析,做用是告诉shell程序使用#! 后面路径指定的解释器来执行本文件,并把当前文件做为解释器的参数。因此 ./hello.sh 做用与 /bin/zsh hellow.sh(或 /bin/zsh $PWD/hello.sh)等效。web

◎ 执行js文件

执行shell脚本的方式,一样适用js文件和其余文件的执行,只要指定相应的解释程序(即程序命令)。如一个简单的node文件,hello.jsshell

console.log('hello world!')
复制代码

能够这样执行(已经安装了node)npm

>> node hello.js
hello world! 
# or 
>> /usr/local/bin/node hello.js 
# 根据不一样系统,可能node命令的路径不同,这里使用的是Mac 
hello world!
复制代码

也可使用使用Shebang的方式,hello.js增长一行,告诉shell程序,用node解释器执行该文档,并给文件增长执行权限json

#! /usr/local/bin/node
console.log('hello world!')

// 第一行也能够这样
// #! /usr/bin/env node 
// 跟上面的区别是,会使用最早出如今环境变量的node解释器
复制代码

就能够这样执行gulp

>> ./hello.js # or $PWD/hello.js
hello world!
复制代码

既然可使用./hello.sh(./hello.js)直接执行文件,那是否可使用hello.sh(hello.js)来直接执行呢?固然是能够的。

一个方式就是把当前目录下的hello.js 移动到当前环境变量的任一目录下(使用echo $PATH 查看当前配置的环境变量)或者把当前目录添加进环境变量,这样就能够直接test.js来执行了,由于shell能够经过环境变量目录检索到test.js这个文件,不只能够在当前目录直接使用,也能够在其余任何目录使用,跟其余命令同样。 固然这并非推荐的作法,并且确实很差。

更好的方式是使用符号连接(Symbolic link)

◎ 符号连接与package.json的bin字段

符号连接(Symbolic link)或软连接,是一种特殊的文件,包含一条以绝对路径或相对路径形式指向其余文件或目录的引用,跟快捷方式的功能相似。继续以hello.js做为示例

上面谈到,为了能直接使用文件名hello.js的方式执行,一种方式是让文件处于shell程序经过环境变量可检索到的目录内;另外一种便是经过对文件创建符号连接的方式,使用ln命令

  • 建立符号连接
# 建立symbolic命令语法,-s为建立symbolic连接
# ln -s /path/to/file /path/to/symbolic 
ln -s $PWD/hello.js /usr/local/bin/hello.js
复制代码

这里把符号连接文件放入环境变量路径下面,这样在当前用户任意目录下面,均可以经过hello.js命令执行,固然,若是符号连接不在环境变量下,则执行方式仍是同样的./hello.js。 符号连接的名字是能够随意的,只要不与现有符号连接重名就好

# 建立全局的符号连接后
>> hello.js
hello world!
# 改成其余名字 
>> ln -s $PWD/hello.js /usr/local/bin/hello
>> hello
hello world!
复制代码
  • 删除符号连接 删除符号连接的方式与删除文件同样,只是删除符号文件,对原文件没有影响
rm /usr/local/bin/hello
复制代码
  • packge.json的bin字段
    有了以上关于符号连接、文件执行、环境变量相关的概念。则npm包命令的执行就很好理解了。以全局方式安装的npm包,npm会在安装的时候在/usr/local/bin/ 目录(通常会在环境变量里面)下面建立bin字段所指定的symbolic,如package.json的bin配置为
{ "bin" : { "myapp" : "./cli.js" } }
复制代码

则会建立 /usr/local/bin/myapp 符号连接指向可执行的cli.js。若是是以本地方式安装,则会在项目下的node_modules/.bin/myapp 下面建立符号连接(这个只能再node_modules/.bin 目录下经过./myapp 方式执行)。myapp命令执行的实际上是cli.js,且官方也要求须要写Shebang#! /use/bin/env node,指定解释器为node

参考:

相关文章
相关标签/搜索