1. 程式人生 > >鳥哥的 Linux 私房菜Shell Scripts篇(一)

鳥哥的 Linux 私房菜Shell Scripts篇(一)

12.1 什麼是 Shell scripts

什麼是 shell script (程式化指令碼) 呢?就字面上的意義,我們將他分為兩部份。 在“ shell
”部分,我們在 十章的 BASH 當中已經提過了,那是一個命令列下面讓我們與系統溝通的一個
工具介面。那麼“ script ”是啥? 字面上的意義, script 是“指令碼、劇本”的意思。整句話是說,
shell script 是針對 shell 所寫的“劇本!”
什麼東西啊?其實, shell script 是利用 shell 的功能所寫的一個“程式 (program)”,這個程
序是使用純文字檔案,將一些 shell 的語法與指令(含外部指令)寫在裡面, 搭配正則表達
式、管線命令與資料流重導向等功能,以達到我們所想要的處理目的。
所以,簡單的說, shell script 就像是早期 DOS 年代的批處理檔案 (.bat) ,最簡單的功能
就是將許多指令彙整寫在一起, 讓使用者很輕易的就能夠 one touch 的方法去處理複雜的動
作 (執行一個檔案 "shell script" ,就能夠一次執行多個指令)。 而且 shell script 更提供陣
列、迴圈、條件與邏輯判斷等重要功能,讓使用者也可以直接以 shell 來撰寫程式,而不必使
用類似 C 程式語言等傳統程式撰寫的語法呢!
這麼說你可以瞭解了嗎?是的! shell script 可以簡單的被看成是批處理檔案, 也可以被說成
是一個程式語言,且這個程式語言由於都是利用 shell 與相關工具指令, 所以不需要編譯即可
執行,且擁有不錯的除錯 (debug) 工具,所以,他可以幫助系統管理員快速的管理好主
機。

12.1.1 幹嘛學習 shell scripts

這是個好問題:“我又幹嘛一定要學 shell script ?我又不是資訊人,沒有寫程式的概念, 那我
幹嘛還要學 shell script 呢?不要學可不可以啊?”呵呵~如果 Linux 對你而言, 你只是想
要“會用”而已,那麼,不需要學 shell script 也還無所謂,這部分先給他跳過去, 等到有空的
時候,再來好好的瞧一瞧。但是,如果你是真的想要玩清楚 Linux 的來龍去脈, 那麼 shell
script 就不可不知,為什麼呢?因為:
自動化管理的重要依據
不用鳥哥說你也知道,管理一部主機真不是件簡單的事情,每天要進行的任務就有:查詢登
錄檔案、追蹤流量、監控使用者使用主機狀態、主機各項硬體裝置狀態、 主機軟體更新查
詢、更不要說得應付其他使用者的突然要求了。而這些工作的進行可以分為: (1)自行手動
處理,或是 (2)寫個簡單的程式來幫你每日“自動處理分析”這兩種方式,你覺得哪種方式比
較好? 當然是讓系統自動工作比較好,對吧!呵呵~這就得要良好的 shell script 來幫忙的
啦!
追蹤與管理系統的重要工作 

12.1 什麼是 Shell scripts

雖然我們還沒有提到服務啟動的方法,不過,這裡可以先提一下,我們 CentOS 6.x 以前的版
本中,系統的服務 (services) 啟動的介面是在 /etc/init.d/ 這個目錄下,目錄下的所有檔案
都是 scripts ; 另外,包括開機 (booting) 過程也都是利用 shell script 來幫忙搜尋系統的相
關設定資料, 然後再代入各個服務的設定引數啊!舉例來說,如果我們想要重新啟動系統登
錄檔案, 可以使用:“/etc/init.d/rsyslogd restart”,那個 rsyslogd 檔案就是 script 啦!
另外,鳥哥曾經在某一代的 Fedora 上面發現,啟動 MySQL 這個資料庫服務時,確實是可以
啟動的, 但是螢幕上卻老是出現“failure”!後來才發現,原來是啟動 MySQL 那個 script 會主
動的以“空的密碼”去嘗試登陸 MySQL ,但為了安全性鳥哥修改過 MySQL 的密碼?~當然就
登陸失敗~ 後來改了改 script ,就略去這個問題啦!如此說來, script 確實是需要學習的
啊!
時至今日,雖然 /etc/init.d/* 這個指令碼啟動的方式 (systemV) 已經被新一代的 systemd 所取
代 (從 CentOS 7 開始), 但是很多的個別服務在管理他們的服務啟動方面,還是使用 shell
script 的機制喔!所以,最好還是能夠熟悉啦!
簡單入侵偵測功能
當我們的系統有異狀時,大多會將這些異狀記錄在系統記錄器,也就是我們常提到的“系統登
錄檔案”, 那麼我們可以在固定的幾分鐘內主動的去分析系統登入檔案,若察覺有問題,就立
刻通報管理員, 或者是立刻加強防火牆的設定規則,如此一來,你的主機可就能夠達到“自我
保護”的聰明學習功能啦~ 舉例來說,我們可以通過 shell script 去分析“當該封包嘗試幾次還
是連線失敗之後,就予以抵擋住該 IP”之類的舉動,例如鳥哥寫過一個關於抵擋砍站軟體的
shell script , 就是用這個想法去達成的呢!
連續指令單一化
其實,對於新手而言, script 最簡單的功能就是:“彙整一些在 command line 下達的連續指
令,將他寫入 scripts 當中,而由直接執行 scripts 來啟動一連串的 command line 指令輸
入!”例如: 防火牆連續規則 (iptables),開機載入程式的專案 (就是在 /etc/rc.d/rc.local
裡頭的資料) ,等等都是相似的功能啦! 其實,說穿了,如果不考慮 program 的部分,那麼
scripts 也可以想成“僅是幫我們把一大串的指令彙整在一個檔案裡面, 而直接執行該檔案就可
以執行那一串又臭又長的指令段!”就是這麼簡單啦!
簡易的資料處理
由前一章正則表示式的 awk 程式說明中, 你可以發現, awk 可以用來處理簡單的資料資料
呢!例如薪資單的處理啊等等的。 shell script 的功能更強大,例如鳥哥曾經用 shell script 直
接處理資料資料的比對啊, 文字資料的處理啊等等的,撰寫方便,速度又快(因為在 Linux
效能較佳),真的是很不錯用的啦!
舉例來說,鳥哥每學期都得要以學生的學號來建立他們能夠操作 Linux 的系統帳號,然後每
個帳號還得要能夠有磁碟容量的限制 (quota) 以及相關的設定等等, 那因為學校的校務系
統提供的資料都是一整串學生資訊,並沒有單純的學號欄位,所以鳥哥就得要通過前幾章的

12.1 什麼是 Shell scripts


方法搭配 shell script 來自動處理相關設定流程, 這樣才不會每學期都頭疼一次啊!
跨平臺支援與學習歷程較短
幾乎所有的 Unix Like 上面都可以跑 shell script ,連 MS Windows 系列也有相關的 script 仿
真器可以用, 此外, shell script 的語法是相當友好的,看都看的懂得文字 (雖然是英文),
而不是機器碼, 很容易學習~這些都是你可以加以考慮的學習點啊!
上面這些都是你考慮學習 shell script 的特點~此外, shell script 還可以簡單的以 vim 來直接
編寫,實在是很方便的好東西!所以,還是建議你學習一下啦。
不過,雖然 shell script 號稱是程式 (program) ,但實際上, shell script 處理資料的速度上
是不太夠的。因為 shell script 用的是外部的指令與 bash shell 的一些預設工具,所以,他常
常會去呼叫外部的函式庫,因此,運算速度上面當然比不上傳統的程式語言。 所以?, shell
script 用在系統管理上面是很好的一項工具,但是用在處理大量數值運算上, 就不夠好了,
因為 Shell scripts 的速度較慢,且使用的 CPU 資源較多,造成主機資源的分配不良。還好,
我們通常利用 shell script 來處理伺服器的偵測,倒是沒有進行大量運算的需求啊!所以不必
擔心的啦!


12.1.2 第一支 script 的撰寫與執行

如同前面講到的,shell script 其實就是純文字檔案,我們可以編輯這個檔案,然後讓這個文
件來幫我們一次執行多個指令, 或者是利用一些運算與邏輯判斷來幫我們達成某些功能。所
以啦,要編輯這個檔案的內容時,當然就需要具備有 bash 指令下達的相關認識。下達指令需
要注意的事項在第四章的開始下達指令小節內已經提過,有疑問請自行回去翻閱。 在 shell
script 的撰寫中還需要用到下面的注意事項:
1. 指令的執行是從上而下、從左而右的分析與執行;
2. 指令的下達就如同第四章內提到的: 指令、選項與引數間的多個空白都會被忽略掉;
3. 空白行也將被忽略掉,並且 [tab] 按鍵所推開的空白同樣視為空白鍵;
4. 如果讀取到一個 Enter 符號 (CR) ,就嘗試開始執行該行 (或該串) 命令;
5. 至於如果一行的內容太多,則可以使用“ [Enter] ”來延伸至下一行;
6. “ # ”可做為註解!任何加在 # 後面的資料將全部被視為註解文字而被忽略!
如此一來,我們在 script 內所撰寫的程式,就會被一行一行的執行。現在我們假設你寫的這
個程式檔名是 /home/dmtsai/shell.sh 好了,那如何執行這個檔案?很簡單,可以有下面幾
個方法:
直接指令下達: shell.sh 檔案必須要具備可讀與可執行 (rx) 的許可權,然後:
絕對路徑:使用 /home/dmtsai/shell.sh 來下達指令;
相對路徑:假設工作目錄在 /home/dmtsai/ ,則使用 ./shell.sh 來執行
變數“PATH”功能:將 shell.sh 放在 PATH 指定的目錄內,例如: ~/bin/
以 bash 程式來執行:通過“ bash shell.sh ”或“ sh shell.sh ”來執行
鳥哥的 Linux 私房菜:基礎學習篇 第四版
599 12.1 什麼是 Shell scripts
反正重點就是要讓那個 shell.sh 內的指令可以被執行的意思啦! 咦!那我為何需要使用
“./shell.sh ”來下達指令?忘記了嗎?回去第十章內的指令搜尋順序察看一下, 你就會知道原
因了!同時,由於 CentOS 預設使用者主資料夾下的 ~/bin 目錄會被設定到 ${PATH} 內,所
以你也可以將 shell.sh 建立在 /home/dmtsai/bin/ 下面 ( ~/bin 目錄需要自行設定) 。此時,
若 shell.sh 在 ~/bin 內且具有 rx 的許可權,那就直接輸入 shell.sh 即可執行該指令碼程式!
那為何“ sh shell.sh ”也可以執行呢?這是因為 /bin/sh 其實就是 /bin/bash (連結檔案),使
用 sh shell.sh 亦即告訴系統,我想要直接以 bash 的功能來執行 shell.sh 這個檔案內的相關
指令的意思,所以此時你的 shell.sh 只要有 r 的許可權即可被執行喔!而我們也可以利用 sh 的
引數,如 -n 及 -x 來檢查與追蹤 shell.sh 的語法是否正確呢! ^_^
撰寫第一支 script
在武俠世界中,不論是那個門派,要學武功要從掃地與蹲馬步做起,那麼要學程式呢?呵
呵,肯定是由“秀出 Hello World!” 這個字眼開始的!OK!那麼鳥哥就先寫一支 script 給大家
瞧一瞧:
[[email protected] ~]$ mkdir bin; cd bin
[[email protected] bin]$ vim hello.sh
#!/bin/bash
# Program:
# This program shows "Hello World!" in your screen.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0
在本章當中,請將所有撰寫的 script 放置到你主資料夾的 ~/bin 這個目錄內,未來比較好管理
啦!上面的寫法當中,鳥哥主要將整個程式的撰寫分成數段,大致是這樣:
1. 第一行 #!/bin/bash 在宣告這個 script 使用的 shell 名稱: 因為我們使用的是 bash ,所
以,必須要以“ #!/bin/bash ”來宣告這個檔案內的語法使用 bash 的語法!那麼當這個程
序被執行時,他就能夠載入 bash 的相關環境配置檔案 (一般來說就是 non-login shell
的 ~/.bashrc), 並且執行 bash 來使我們下面的指令能夠執行!這很重要的!(在很多
狀況中,如果沒有設定好這一行, 那麼該程式很可能會無法執行,因為系統可能無法判
斷該程式需要使用什麼 shell 來執行啊!)
2. 程式內容的說明: 整個 script 當中,除了第一行的“ #! ”是用來宣告 shell 的之外,其他的
# 都是“註解”用途! 所以上面的程式當中,第二行以下就是用來說明整個程式的基本數
據。一般來說, 建議你一定要養成說明該 script 的:1. 內容與功能; 2. 版本資訊; 3.
作者與聯絡方式; 4. 建立日期;5. 歷史紀錄 等等。這將有助於未來程式的改寫與 debug
呢!
3. 主要環境變數的宣告: 建議務必要將一些重要的環境變數設定好,鳥哥個人認為,
PATH 與 LANG (如果有使用到輸出相關的資訊時) 是當中最重要的! 如此一來,則可
讓我們這支程式在進行時,可以直接下達一些外部指令,而不必寫絕對路徑呢!比較方

12.1 什麼是 Shell scripts

便啦!
4. 主要程式部分 就將主要的程式寫好即可!在這個例子當中,就是 echo 那一行啦!
5. 執行成果告知 (定義回傳值) 是否記得我們在第十章裡面要討論一個指令的執行成功與
否,可以使用 $? 這個變數來觀察~ 那麼我們也可以利用 exit 這個指令來讓程式中斷,
並且回傳一個數值給系統。 在我們這個例子當中,鳥哥使用 exit 0 ,這代表離開 script
並且回傳一個 0 給系統, 所以我執行完這個 script 後,若接著下達 echo $? 則可得到 0
的值喔! 更聰明的讀者應該也知道了,呵呵!利用這個 exit n (n 是數字) 的功能,我
們還可以自訂錯誤訊息, 讓這支程式變得更加的 smart 呢!
接下來通過剛剛上頭介紹的執行方法來執行看看結果吧!
[[email protected] bin]$ sh hello.sh
Hello World !
你會看到螢幕是這樣,而且應該還會聽到“咚”的一聲,為什麼呢?還記得前一章提到的 printf
吧?用 echo 接著那些特殊的按鍵也可以發生同樣的事情~ 不過, echo 必須要加上 -e 的選
項才行!呵呵!在你寫完這個小 script 之後,你就可以大聲的說:“我也會寫程式了”!哈哈!
很簡單有趣吧~ ^_^
另外,你也可以利用:“chmod a+x hello.sh; ./hello.sh”來執行這個 script 的呢!


12.1.3 撰寫 shell script 的良好習慣建立

一個良好習慣的養成是很重要的~大家在剛開始撰寫程式的時候,最容易忽略這部分, 認為
程式寫出來就好了,其他的不重要。其實,如果程式的說明能夠更清楚,那麼對你自己是有
很大的幫助的。
舉例來說,鳥哥自己為了自己的需求,曾經撰寫了不少的 script 來幫我進行主機 IP 的偵測
啊、 登入檔案分析與管理啊、自動上傳下載重要配置檔案啊等等的,不過,早期就是因為太
懶了, 管理的主機又太多了,常常同一個程式在不同的主機上面進行更改,到最後,到底哪
一支才是最新的都記不起來, 而且,重點是,我到底是改了哪裡?為什麼做那樣的修改?都
忘的一乾二淨~真要命~
所以,後來鳥哥在寫程式的時候,通常會比較仔細的將程式的設計過程給他記錄下來,而且
還會記錄一些歷史紀錄, 如此一來,好多了~至少很容易知道我修改了哪些資料,以及程式
修改的理念與邏輯概念等等, 在維護上面是輕鬆很多很多的喔!
另外,在一些環境的設定上面,畢竟每個人的環境都不相同,為了取得較佳的執行環境, 我
都會自行先定義好一些一定會被用到的環境變數,例如 PATH 這個玩意兒! 這樣比較好啦~
所以說,建議你一定要養成良好的 script 撰寫習慣,在每個 script 的檔案開始處記錄好:
script 的功能;
script 的版本資訊;
12.1 什麼是 Shell scripts
script 的作者與聯絡方式;
script 的版權宣告方式;
script 的 History (歷史紀錄);
script 內較特殊的指令,使用“絕對路徑”的方式來下達;
script 執行時需要的環境變數預先宣告與設定。
除了記錄這些資訊之外,在較為特殊的程式碼部分,個人建議務必要加上註解說明,可以幫
助你非常非常多! 此外,程式碼的撰寫最好使用巢狀方式,在包覆的內部程式碼最好能以
[tab] 按鍵的空格向後推, 這樣你的程式碼會顯的非常的漂亮與有條理!在查閱與 debug 上較
為輕鬆愉快喔! 另外,使用撰寫 script 的工具最好使用 vim 而不是 vi ,因為 vim 會有額外的
語法檢驗機制,能夠在第一階段撰寫時就發現語法方面的問題喔!