node.js 命令行工具(cli)

一. 先了解一下package.json

每一個項目的根目錄都有一個package.json文件,定義了這個項目所須要的各類模塊,以及項目的配置信息,下面是一個比較完整的package.json文件
vue

{
  "name": "vue-cli",
  "version": "2.9.3",
  "description": "A simple CLI for scaffolding Vue.js projects.",
  "preferGlobal": true,
  "bin": {
    "vue": "bin/vue",
    "vue-init": "bin/vue-init",
    "vue-list": "bin/vue-list"
  },
  "repository": {
    "type": "",
    "url": ""
  },
  "keywords": [
  ],
  "author": "litongqian",
  "license": "MIT",
  "bugs": {
    "url": ""
  },
  "homepage": "",
  "scripts": {
    "test": "npm run lint && npm run e2e",
    "start": "node index.js"
  },
  "dependencies": {
    "async": "^2.4.0",
    "chalk": "^2.1.0",
  },
  "devDependencies": {
    "chai": "^4.1.2",
    "eslint": "^3.19.0",
  },
  "engines": {
    "node": ">=6.0.0"
  }
}
複製代碼
1. 其中scripts字段

指定了運行腳本命令的npm命令行縮寫,好比start指定了運行npm run start時,所要執行的命令。
node

2. bin字段vue-cli

bin項用來指定各個內部命令對應的可執行文件的位置shell

"bin": {
    "vue": "bin/vue",
    "vue-init": "bin/vue-init",
    "vue-list": "bin/vue-list"
  },複製代碼

上面代碼指定,vue 命令對應的可執行文件爲 bin 子目錄下的vue。npm

3. npm linkjson

項目目錄:數組


本身開發模塊的時候,好比上面的mymodule模塊,若是想在hello中使用,咱們能夠採用相對路徑require('./mymodule')加載模塊。bash

也可使用自定義模塊標識require('mymodule'),Node加載自定義模塊須要將其安裝到全局的或項目的node_modules目錄之中。對於上述模塊,解決方法就是在全局的node_modules目錄之中,生成一個符號連接,指向模塊的本地目錄。async

npm link就能起到這個做用,會自動創建這個符號連接。函數

  • 請設想這樣一個場景,你開發了一個模塊mymodule,目錄爲hello/mymodule,你本身的項目要用到這個模塊,首先,在模塊目錄(src/mymodule)下運行npm link命令。
hello/mymodule$ npm link
複製代碼


上面的命令會在NPM的全局模塊目錄內,生成一個符號連接文件,該文件的名字就是package.json文件中指定的模塊名。

/usr/local/lib/node_modules/mymodule -> /Users/tongqianli/Desktop/work/hello/mymodule複製代碼

這個時候,已經可使用mymodule模塊了。

這時能夠經過添加path解決:

操做系統中都會有一個PATH環境變量,系統調用一個命令的時候,就會在PATH變量中註冊的路徑中尋找,若是註冊的路徑中有就調用,不然就提示命令沒找到。

-> export PATH=$PATH: # 將 /usr/bin 追加到 PATH 變量中
-> export NODE_PATH="/usr/lib/node_modules;/usr/local/lib/node_modules" #指定 NODE_PATH 變量
複製代碼

NODE_PATH 就是NODE中用來尋找模塊所提供的路徑註冊環境變量。咱們可使用上面的方法指定NODE_PATH環境變量。

更好的辦法:

切換到項目目錄,再次運行npm link命令,並指定模塊名。

hello tongqianli$ npm link mymodule複製代碼

上面命令生成了本地node_modules/mymodule模塊映射到全局的node_modules/mymodule ,全局,全局的映射到本地hello/mymodule。

/Users/tongqianli/Desktop/work/hello/node_modules/mymodule 
-> /usr/local/lib/node_modules/mymodule 
-> /Users/tongqianli/Desktop/work/hello/mymodule複製代碼

而後,就能夠在你的項目中,加載該模塊了。

var myModule = require('myModule');
複製代碼

這樣一來,mymodule的任何變化,均可以直接反映在項目之中。可是,這樣也出現了風險,任何在目錄中對mymodule的修改,都會反映到模塊的源碼中。

若是你的項目再也不須要該模塊,能夠在項目目錄內使用npm unlink命令,刪除符號連接。

hello tongqianli$ npm unlink mymodule複製代碼

二. 可執行腳本

寫一個簡單的腳本hello

$ mkdir hello #建立一個文件夾 
$ cd hello && touch  hello #建立命令文件

#!/usr/bin/env node
console.log('hello world');
複製代碼

文件的頭部加入#!/usr/bin/env node這行代碼,這裏表示使用node做爲腳本的解釋程序,能夠省略node ./hello,直接運行./hello, node的路徑經過env來查找,能夠避免node安裝的路徑不同帶來找不到的問題。

打開/usr/bin/env,能夠查看到PATH,操做系統經過路徑找到node


而後,修改 hello 的權限。

$ chmod 755 hello
$./hello複製代碼


若是想把 hello 前面的路徑去除,能夠將 hello 的路徑加入環境變量 PATH。可是,另外一種更好的作法,是在當前目錄下新建 package.json ,寫入下面的內容。

{
  "name": "hello",
  "bin": {
    "hello": "./hello"
  }
}複製代碼

而後執行 npm link 命令。不明白的看上面

$ npm link複製代碼



執行後會產生一個全局的映射關係,可執行腳本bin/hello映射到全局node_modules/hello/hello,全局的node_modules/hello映射到本地hello,這樣就可使用hello了

三.命令行參數

命令行參數能夠用系統變量 process.argv 獲取。

修改hello腳本

#!/usr/bin/env node
console.log('hello ', process.argv);複製代碼

其中process爲node進程中的全局變量,process.argv爲一數組,數組內存儲着命令行的各個部分,argv[0]爲node的安裝路徑,argv[1]爲主模塊文件路勁,剩下爲子命令或參數,以下:


$ hello a b c
# process.argv的值爲[ '/usr/local/bin/node', '/usr/local/bin/hello', 'a', 'b', 'c' ]


腳本能夠經過 child_process 模塊新建子進程,從而執行 Unix 系統命令,修改hello

exec方法用於執行bash命令,exec方法最多能夠接受兩個參數,第一個參數是所要執行的shell命令,第二個參數是回調函數,該函數接受三個參數,分別是發生的錯誤、標準輸出的顯示結果、標準錯誤的顯示結果。


#!/usr/bin/env node
var name = process.argv[2];
var exec = require('child_process').exec;

var child = exec('echo hello ' + name, function(err, stdout, stderr) {
  if (err) throw err;
  console.log(stdout);
});複製代碼

執行$ hello litongqian

若是咱們想查看全部文件,修改hello

var name = process.argv[2];
var exec = require('child_process').exec;

var child = exec(name, function(err, stdout, stderr) {
  if (err) throw err;
  console.log(stdout);
});複製代碼

執行$ hello ls 

hello目錄下有三個文件


4、shelljs 模塊

shell.js 模塊從新包裝了 child_process,調用系統命令更加方便。它須要安裝後使用。

npm install --save shelljs
複製代碼

而後,改寫腳本。

#!/usr/bin/env node
var name = process.argv[2];
var shell = require("shelljs");

shell.exec("echo hello " + name);複製代碼

5、yargs 模塊

shelljs 只解決了如何調用 shell 命令,而 yargs 模塊可以解決如何處理命令行參數。它也須要安裝。

$ npm install --save yargs
複製代碼

yargs 模塊提供 argv 對象,用來讀取命令行參數。請看改寫後的 hello 。

#!/usr/bin/env node
var argv = require('yargs').argv;

console.log('hello ', argv.name);
複製代碼

使用時,下面兩種用法均可以。

$ hello --name=tom
hello tom

$ hello --name tom
hello tom
複製代碼

也就是說,process.argv 的原始返回值以下。

$ node hello --name=tom
[ 'node',
  '/usr/local/bin/hell',
  '--name=tom' ]
複製代碼

yargs 能夠上面的結果改成一個對象,每一個參數項就是一個鍵值對。

六.發佈命令包

經過npm publish進行發佈,前提是有npm賬號。如何操做能夠查看npm 官方文檔

本文是經過原生node.js來開發命令工具,而vue-cli是採用commander.js來簡化命令工具開發,

瞭解了執行流程,學習對應的模塊,就能夠了!,本文拋個磚頭


最後:有時咱們用到的命令行不是全局安裝的,而是本地安裝的

1. package.json bin字段

bin項用來指定各個內部命令對應的可執行文件的位置。

"name":"someTool",
"bin": {
  "someTool": "./bin/someTool.js"
}
複製代碼

上面代碼指定,someTool 命令對應的可執行文件爲 bin 子目錄下的 someTool.js。

當一個項目依賴上面的someTool工具時,同時只是本地安裝

{
  "name": "myproject",
  "devDependencies": {
    "someTool": "latest"
  },
  "scripts": {
    start: 'someTool build'  //等同於start: './node_modules/someTool/someTool.js build'

  }
}複製代碼

npm會尋找這個文件,在node_modules/.bin/目錄下創建符號連接。在上面的例子中,someTool.js會創建符號連接npm_modules/.bin/someTool。因爲node_modules/.bin/目錄會在運行時加入系統的PATH變量,所以在運行npm時,就能夠不帶路徑,直接經過命令來調用這些腳本。

所以,像上面這樣的寫法能夠採用簡寫。

scripts: {  
  start: './node_modules/someTool/someTool.js build'
}

// 簡寫爲

scripts: {  
  start: 'someTool build'
}

複製代碼

全部node_modules/.bin/目錄下的命令,均可以用npm run [命令]的格式運行。在命令行下,鍵入npm run,而後按tab鍵,就會顯示全部可使用的命令。

1. npm run

上面代碼中,scripts字段指定了兩項命令

start
,輸入 npm run-script start或者 npm run start,就會執行  someTool buildnpm runnpm run-script的縮寫,通常都使用前者,可是後者能夠更好地反應這個命令的本質。

npm run命令會自動在環境變量$PATH添加node_modules/.bin目錄,因此scripts字段裏面調用命令時不用加上路徑,這就避免了全局安裝NPM模塊。

npm run若是不加任何參數,直接運行,會列出package.json裏面全部能夠執行的腳本命令。

npm內置了兩個命令簡寫,npm test等同於執行npm run testnpm start等同於執行npm run start

npm run會建立一個Shell,執行指定的命令,並臨時將node_modules/.bin加入PATH變量,這意味着本地模塊能夠直接運行。

舉例來講,你執行ESLint的安裝命令。

$ npm i eslint --save-dev
複製代碼

運行上面的命令之後,會產生兩個結果。首先,ESLint被安裝到當前目錄的node_modules子目錄;其次,node_modules/.bin目錄會生成一個符號連接node_modules/.bin/eslint,指向ESLint模塊的可執行腳本。

而後,你就能夠在package.jsonscript屬性裏面,不帶路徑的引用eslint這個腳本。

{
  "name": "Test Project",
  "devDependencies": {
    "eslint": "^1.10.3"
  },
  "scripts": {
    "lint": "eslint ."
  }
}
複製代碼

等到運行npm run lint的時候,它會自動執行./node_modules/.bin/eslint .

若是直接運行npm run不給出任何參數,就會列出scripts屬性下全部命令。

$ npm run
Available scripts in the user-service package:
  lint
     jshint **.js
  test
    mocha test/複製代碼

到此,結束!

相關文章
相關標籤/搜索