以全局方式安裝的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
參考: