前言
最近在公司使用Jenkins自動化編譯前端、Android、iOS時遇到了挺多的shell腳本的坑,以前都是從網上找一些腳本改改測試可用就直接用了,但是最近項目變化大,導緻自動化編譯總是出錯,于是決定好好學習下shell腳本如何正确的編寫!以下是我個人的實際項目所用的一些總結,我大緻會圍繞三個問題來聊聊我遇到的坑和解決方法:
- 如何根據git的送出記錄,判斷代碼的變化,決定是否需要編譯!
- 檢測指令是否存在,指令不存在時如何捕獲錯誤?
- 判斷一條指令的執行結果,根據結果判斷是否需要特殊處理
由于本人隻是一個前端開發者,對于Linux的shell腳本還處于一臉懵逼狀态,可能我的方法和您的不一緻,如果您感覺我的方法和思路有什麼不對的地方還請大神給予指正~
1. 如何根據git的送出記錄,判斷代碼的變化,決定是否需要編譯!
首先将可用代碼貼出來:
#擷取上次送出和本次送出的差異
git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT > ${WORKSPACE}/${APP_ID}commint.log
rowNum=$(awk 'END{print NR}' "${WORKSPACE}/${APP_ID}commint.log")
# 檔案更新數 小于等于 0 無需建構
if [ $rowNum -le 0 ];then
echo "代碼沒有任何修改,項目無需建構"
exit 0
fi
1.1 從第一句話開始
git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT > ${WORKSPACE}/${APP_ID}commint.log
這句話實作了兩個功能:
- 得到上次git送出和本次送出的差異檔案
git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT
- 将得到的差異輸出到一個檔案中
> ${WORKSPACE}/${APP_ID}commint.log
檢視git兩次送出的差異
我們知道git diff 可以得到檔案的差異,但是這裡我們隻需要得到差異的檔案名稱就可以,根據git官網文檔得到
--name-only
參數可得到檔案名稱,那麼
$GIT_PREVIOUS_SUCCESSFUL_COMMIT 和 $GIT_COMMIT
是什麼呢?
根據Jenkins官方文檔中的可用的shell變量一文得知
- $GIT_PREVIOUS_SUCCESSFUL_COMMIT :代表上次git送出的commit ID
- $GIT_COMMIT :代表本次git送出的 commit ID
- ${WORKSPACE} : Jenkins的工作空間絕對位址
- ${APP_ID} : 目前Jenkins的任務名稱
OK,得到兩次送出的差異檔案清單之後為友善後續使用,我們可以利用shell 中的
>
重定向功能把結果輸出到一個檔案中!
參考shell腳本程式設計文檔得知:
-
将前一個指令的标準輸出結果 以文本替換的方式>
進一個檔案中,當檔案不存在時自動建立該檔案重寫
-
将前一個指令的标準輸出以>>
的形式,追加進一個檔案的末尾行,當檔案不存在時自動建立該檔案追加
最終利用
git diff --name-only $GIT_PREVIOUS_SUCCESSFUL_COMMIT $GIT_COMMIT > ${WORKSPACE}/${APP_ID}commint.log
句話我們得到了git上次送出跟本次送出的差異檔案,并将結果輸出到了目前Jenkins任務的工作空間根目錄,檔案名為commit.log裡!
1.2 第二句 rowNum=$(awk 'END{print NR}' "${WORKSPACE}/${APP_ID}commint.log")
rowNum=$(awk 'END{print NR}' "${WORKSPACE}/${APP_ID}commint.log")
我們已經得到了差異的檔案資訊并寫入到了commit.log檔案裡,那麼隻需要讀取這個檔案,統計下這個檔案裡有多少行,是不是就可以得到本次修改了多少個檔案?
根據《Linux+shell腳本攻略(第二版)》一書第四章 讓文本飛 一文中得知 使用awk 可以統計檔案的行數!
awk簡介
awk特殊變量
使用awk統計檔案中的行數
是以我們得到了這條統計檔案内容行數的指令,并将改指令的結果賦予
rowNum
變量
$ rowNum=$(awk 'END{print NR}' "${WORKSPACE}/${APP_ID}commint.log")
1.3 接下來的檔案内容數量的判斷
根據《Linux指令行于shell程式設計大全(第三版)》中 第12章 使用結構化指令 一文中得知:
使用結構化語句
數值比較
if [ $rowNum -le 0 ];then
echo "代碼沒有任何修改,項目無需建構"
exit 0
fi
2. 檢測指令是否存在,指令不存在時如何捕獲錯誤?
在使用shell腳本自動化編譯的時候經常會遇到當某個指令不存在或者沒有安裝的時候直接報錯,終止了編譯!現在解決的就是當遇到指令找不到的時候直接安裝該指令
先把最終實作代碼貼出來然後一點點去分析(拿移動端代碼熱更新舉個例子):
#檢測有沒有code-push-cli,沒有直接全局安裝
if hash code-push 2>/dev/null; then
echo "有code-push-cli"
else
npm install [email protected] -g
fi
參考自stackoverflow 方法大緻有三種:
command -v <the_command>
hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords
- command :shell内建函數,-v 參數主要用于檢測指令是否存在,指令建構的退出狀态是指令的退出狀态
- type : shell内建函數,該指令用來顯示指定指令的類型,判斷給出的指令為“外部指令”、“指令别名”或者“内部指令”
- hash : shell内建函數,hash指令的作用是在環境變量PATH中搜尋指令name的完整路徑并記住它,這樣以後再次執行相同的指令時,就不必搜尋其完整路徑了,成功執行時,hash指令的退出狀态為0
以上三種方式随意一個就可以了,看個人愛好了!現在來看下
2>/dev/null
的作用,在開始之前首先需要簡單說下什麼是
檔案操作符
2.1 檔案描述符
當執行shell指令時,每個 Unix/Linux 指令運作時會預設打開3個檔案,每個檔案有對應的檔案描述符來友善我們使用:
類型 | 檔案描述符 | 預設情況 | 對應檔案句柄位置 |
---|---|---|---|
标準輸入(standard input) | 從鍵盤獲得輸入 | /proc/self/fd/0 | |
标準輸出(standard output | 1 | 輸出到螢幕(即控制台) | /proc/self/fd/1 |
錯誤輸出(error output) | 2 | 輸出到螢幕(即控制台) | /proc/self/fd/2 |
是以我們平時在執行shell指令中,都預設是從鍵盤獲得輸入,并且将結果輸出到控制台上。但是我們可以通過更改檔案描述符預設的指向,進而實作輸入輸出的重定向。比如我們将1指向檔案,那麼标準的輸出就會輸出到檔案中。
2.2 輸出重定向
輸出重定向的使用方式很簡單,基本的一些指令如下:
指令 | 介紹 |
---|---|
command >filename | 把标準輸出重定向到新檔案中 |
command 1>filename | 同上 |
command >>filename | 把标準輸出追加到檔案中 |
command 1>>filename | 同上 |
command 2>filename | 把标準錯誤重定向到新檔案中 |
command 2>>filename | 把标準錯誤追加到新檔案中 |
2.3 >/dev/null
/dev/null代表linux的空裝置檔案,所有往這個檔案裡面寫入的内容都會丢失,俗稱“黑洞”。那麼執行了>/dev/null之後,标準輸出就會不再存在,沒有任何地方能夠找到輸出的内容。
2.4 2>&1
這條指令用到了重定向綁定,采用&可以将兩個輸出綁定在一起。這條指令的作用是錯誤輸出将和标準輸出同用一個檔案描述符,說人話就是錯誤輸出将會和标準輸出輸出到同一個地方。
2.5 >/dev/null 2>&1
linux在執行shell指令之前,就會确定好所有的輸入輸出位置,并且從左到右依次執行重定向的指令,是以>/dev/null 2>&1的作用就是讓标準輸出重定向到/dev/null中(丢棄标準輸出),然後錯誤輸出由于重用了标準輸出的描述符,是以錯誤輸出也被定向到了/dev/null中,錯誤輸出同樣也被丢棄了。
參考自詳解shell中>/dev/null 2>&1到底是什麼
小結
hash code-push 2>/dev/null
用
hash
來判斷
code-push
指令是否存在,并且把标準錯誤重定向到Linux黑洞中,以保證腳本的完整運作
3. 判斷一條指令的執行結果,根據結果判斷是否需要特殊處理
拿移動端代碼熱更新
code-push
舉個例子,根據
code-push whoami
的執行結果判斷目前code-push是否登入,未登入執行登入操作
先貼下代碼:
#登入code-push私服
WHOAMI=`code-push whoami 2>&1`
if echo "$WHOAMI" | grep "userName" >/dev/null; then
echo "code-push login Successfully"
else
code-push login
fi
經過上面對檔案操作符和重定向的學習,在看如上這段代碼是不是好了解多了?
3.1 第一句 WHOAMI=`code-push whoami 2>&1`
WHOAMI=`code-push whoami 2>&1`
正常來說
code-push whoami
輸出的是您目前登入的使用者名,如果目前處于未登入狀态,那麼該指令會使用檔案操作符2 輸出标準的錯誤資訊,那麼我們用到 2>&1 重定向綁定語句将标準錯誤輸出和标準輸出資訊儲存在WHOAMI變量中
3.2 第二句關鍵代碼 echo "$WHOAMI" | grep "userName" >/dev/null
echo "$WHOAMI" | grep "userName" >/dev/null
該語句的關鍵在于 Linux 的管道操作符 和 grep 過濾器
- | : Linux 中的管道操作符,用于将操作符左側的标準輸出資訊,以标準輸入的形式交給操作符右側的指令!(自己的了解,無參考)
- grep : Linux 中用于找到模式比對的行,找到了傳回狀态碼為0,未找到傳回狀态碼未2
>/dev/null
将左側所有的輸出重定向到Linux黑洞
總結
我個人的思路就是捕獲一切可能傳回狀态碼為2的錯誤輸出!保證程式能夠按照預想的正常運作!歡迎拍磚~
轉載于:https://www.cnblogs.com/shuoer/p/9627072.html