一統江湖的大前端(4)shell.js——穿上馬甲我照樣認識你
 >《一統江湖的大前端》系列是自己的前端學習筆記,旨在介紹javascript在非網頁開發領域的應用案例和發現各類好玩的js庫,不定期更新。如果你對前端的理解還是寫寫頁面綁綁事件,那你真的是有點OUT了,前端能做的事情已經太多了, `手機app開發` , `桌面應用開發` , `用於神經網路人工智慧的庫` , `頁面遊戲` , `資料視覺化` , 甚至 `嵌入式開發` ,什麼火就搞什麼,活脫脫一個**蹭熱點小能手**。如果你也覺得前端的日常開發有些枯燥,不妨一起來看看前端的另一番模樣。 > 前端開發人員的工作內容幾乎很少會涉及指令碼的編寫,建議將shell.js和git的命令列指令綜合在一起作為專題學習,集中學習一下常用指令。更詳細的引數請參考專門的shell指令碼語言資料進行學習。 #### 一.Shell && Shelljs 碼農界存在著無數條鄙視鏈,`linux`使用者對`windows`的鄙視便是其中之一,`cli`使用者對`GUI`使用者的嘲諷也是如此,在這樣一個講究**逼格**的時代,如果你的桌面上沒有一個小黑窗時不時地從下往上翻滾並丟擲一些亮綠色的字串,你真不好意思跟人打招呼。而`前端`這種天生幾乎不用和命令列打交道的物種,自然再一次莫名其妙地處在了鄙視鏈的末端,沒錯,是再一次。  `Shell`是`linux`下的指令碼語言解析器,擁有豐富且強大的底層操作許可權。`Shelljs`就是基於`node`的一層命令封裝外掛,讓前端開發者可以不依賴`linux`也不依賴類似於`cmder`的轉換工具,而是直接在我們最熟悉不過的`javascript`程式碼中編寫`shell`命令實現功能。 #### 二.前端開發人員學Shelljs幹嘛 `shell`跟`自動化`是強相關的,個人理解其用途主要是兩方面: + 1.從業務邏輯的需求來看,`shelljs`並不是什麼具有非凡意義的外掛,它只是對`node`的底層API進行了一些封裝,方便我們以類似`shell`的語法去編寫程式碼梳理邏輯,實現一些業務邏輯需求,如果你所在的專案組恰好需要這樣的能力,用它會很方便; + 2.`cli`相對於`GUI`或許是更快,但它依然是一種重複勞作,有了shelljs和全棧能力,開發者可以將團隊中**耗時的重複性常規動作**編寫為自動化指令碼,並利用前端的天然優勢為其配備`GUI`,用頁面上的一鍵點選來替代重複勞作,在緊張的開發節奏中,平均每天為你節約個**30-40**分鐘起來走走喝杯水難道不好嗎? > 想要一統江湖,大前端的深度和廣度是缺一不可的,你可以說你不精通shell,但不要說自己不懂shell,更不要一臉天真地反問面試官“前端還能搞shell?這麼神奇?”他不會覺得你對知識有好奇心,只會覺得你很low,哦不對,是大寫的LOW. #### 三.官方示例(包含註釋) 廢話說完了,開始學習,拿好小本子,我要開車了。 ```javascript //引入shelljs var shell = require('shelljs') //檢查控制檯是否以執行`git `開頭的命令 if (!shell.which('git')) { //在控制檯輸出內容 shell.echo('Sorry, this script requires git'); shell.exit(1); } shell.rm('-rf','out/Release');//強制遞迴刪除`out/Release目錄` shell.cp('-R','stuff/','out/Release');//將`stuff/`中所有內容拷貝至`out/Release`目錄 shell.cd('lib');//進入`lib`目錄 //找出所有的副檔名為js的檔案,並遍歷進行操作 shell.ls('*.js').forEach(function (file) { /* 這是第一個難點:sed流編輯器,建議專題學習,-i表示直接作用原始檔 */ //將build_version欄位替換為'v0.1.2' shell.sed('-i', 'BUILD_VERSION', 'v0.1.2', file); //將包含`REMOVE_THIS_LINE`字串的行刪除 shell.sed('-i', /^.*REMOVE_THIS_LINE.*$/, '', file); //將包含`REPLACE_LINE_WITH_MACRO`字串的行替換為`macro.js`中的內容 shell.sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, shell.cat('macro.js'), file); }); //返回上一級目錄 shell.cd('..'); //run external tool synchronously //即同步執行外部工具 if (shell.exec('git commit -am "Auto-commit"').code !== 0){ shell.echo('Error: Git commit failed'); shell.exit(1); } ``` #### 三.官方示例中涉及的命令解釋: + **shell.which(command)** > 在環境變數`PATH`中尋找指定命令的地址,判斷該命令是否可執行,返回該命令的絕對地址。 + **echo** > 在控制檯輸出指定內容 + **exit(code)** > 以退出碼為`code`退出當前程序 + **rm** > 刪除一個目錄中一個或多個*檔案*或*目錄*,一旦刪除,無法恢復。 `常用引數`: + -f:強制刪除檔案; + -i:刪除之前先詢問使用者; + -r:遞迴處理目錄; + -v:顯示處理過程; + **cp([options,] source_array, dest)** > 用來將一個或多個原始檔或目錄複製到指定的檔案或目錄。 `常用引數`: + -f:強制刪除檔案; + -i:刪除之前先詢問使用者; + -r:遞迴處理目錄; + **cd** > 切換工作目錄至指定的相對路徑或絕對路徑。`cd..`為返回上一級,`cd-`回到前一目錄。 + **ls** > 用來顯示目標列表。 `常用引數`: + -a:顯示所有檔案; + -C:多列顯示查詢結果; + -l:單列長格式顯示查詢結果(與-C相反); + -R:遞迴處理目錄; + **sed([options,] search_regex, replacement, file_array** > 將`file_array`中符合`search_regex`的內容替換為`replacement`,支援正則的捕獲組自引用。一次處理一行內容,處理完成後把緩衝區內容送往螢幕,然後處理下一行,迴圈直至結束。功能豐富且用法較複雜,建議自行百度進行專題學習。 + -i:直接作用原始檔 + **cat** > 將一個或多個檔案內容讀入,指定一個檔案時讀入該檔案,指定多個檔案時將內容連線在一起讀入。 + **exec(command,[, options][, callback])** > 執行所傳入的命令 + async:是否非同步執行,預設`false`,傳入callback時自動開啟 + slient:不輸出資訊到console,預設`false` + encoding:預設`utf8` #### 四.文件中其他API概覽 + **chmod** > 設定檔案呼叫許可權 + **基本語法** :chmod [-cfvR] [--help] [--version] mode file... + -c:若檔案許可權確實被更改,才顯示更改動作 + -f: 許可權無法被更改時不顯示錯誤資訊 + -v: 顯示許可權變更的詳細資料 + -R: 遞迴,對其目錄下所有檔案和子檔案執行相同操作 + **mode欄位格式** : [ugoa...][[+-=][rwxX]...][,...] + u表示該檔案擁有者,g表示同一群體者,o表示其他,a表示所有 + +表示增加許可權,-表示取消許可權,=表示唯一設定許可權 + r表示可讀,w表示可寫,x表示可執行,X表示當該檔案是個子目錄? + **find(path[,path...])** > 尋找路徑 + **grep([options,] regex_filter,file)** > 從指定檔案中抓取符合正則的行 + -v:翻轉正則匹配 + -l:僅列印符合條件的檔名 + **head([{'-n':