以全局方式安装的npm包,如gulp
、webpack
等为何能够像mkdir
、copy
这样的shell命令(或程序)同样,在任何文件夹均可以经过命令行调用?跟环境变量又有什么关系?什么是符号连接?javascript
在解释这些问题前,先了解下linux上文件执行相关内容,会更容易理解些java
最开始接触shell时,通常会说起shell脚本文件的不一样执行方式。先建立有一条简单命令的shell脚本文件hello.sh
node
echo "hello world"
复制代码
/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/zsh
linux
#! /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
执行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)
符号连接(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
复制代码
/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
参考: