第十一章 BASH腳本(一)
常見的Shell變量的類型包括環境變量、預定義變量、位置變量、用戶自定義變量。本節將分別學習這四種Shell變量的使用。
11. 1、 Shell的環境變量
通過set命令可以查看系統中所有Shell變量(包括環境變量和用戶自定義變量),由於內容輸出較多,建議使用less命令分頁查看。
常用環境變量:
PATH 決定了shell將到哪些目錄中尋找命令或程序
HOME 當前用戶主目錄
HISTSIZE 歷史記錄數
LOGNAME 當前用戶的登錄名
USER 當前用戶名
UID 當前用名的UID
HOSTNAME 指主機的名稱
SHELL 前用戶Shell類型
LANGUGE 語言相關的環境變量,多語言可以修改此環境變量
MAIL 當前用戶的郵件存放目錄
PS1 基本提示符,對於root用戶是#,對於普通用戶是$
PS2 附屬提示符,默認是“>”
例:以分號分隔,顯示當前的用戶的用戶名、宿主目錄、登錄Shell。
例:查看當前命令的搜索路徑,並將/opt/bin目錄添加到現有搜索路徑中去,從而可以直接執行此目錄中的命令。
環境變量的配置文件
用戶可在當前的Shell環境中直接為環境變量賦值,但需要長期變更所使用的某個環境變量時,可以修改配置文件。
在Linux系統中,用戶環境變量的設置工作習慣上在 /etc/profile 文件及宿主目錄中 .bash_profile文件中進行,前者稱為全局配置文件(對所有用戶起作用),后者稱為用戶配置文件(允許覆蓋全局配置)。
例:在當前用戶環境中,將用於限制歷史命令的環境變量HISTSIZE的值改為100。
例:編輯“~/.bash_profile”文件,修改PATH的設置,以便用戶在下次登錄后能夠使用服務/opt/bin 目錄作為默認的搜索路徑。
# vi /root/.bash_profiel
PATH=$PATH:$HOME/bin:/opt/bin
Export PATH
11.2 Shell位置變量
為了在使用Linux程序時,方便通過命令行給程序提供操作參數,Bash引入了位置變量的概念。在執行Shell命令操用時,除了輸入的第一字段(命令名或腳本程序名)以外,其余的字符串參數按照從在到右的順序依次賦組位置變量。
需要引用這些位置變量的值時,采用$n的格式,其中n 是參數位置的序號(從1-9)。例如當執行 servive network restart 命令時,service腳本程序的第1個位置參數“$1”表示,對應的值為network,第2 個位置參數用“$2”表示,對應的值為restart。
$0代表所執行命令或腳本程序的名稱,雖然$0與位置變量的格式相同,但是屬於預定義變量而不是位置變量。在編寫腳本程序時,經常會使用位置變量來接收用戶所指定的命令參數。
11.3 預定義變量
預定義變量是由Bash程序預先定義好的一些特殊變量,用戶只能使用預定義變量,而不能創建新的預定義變量,或者直接為預定義變量賦值。所有的預定義變量都是由$符號和別一個符號組成的,較常用的預定義變量包括以下這些。
- $#:表示命令行中位置參數的數量
- $*:表示所有位置參數的內容
- $?:表示命令執行后返回的狀態,用於檢查上一個命令的執行是否正確。在Linix中,命令退出狀態為0表示命令執行正確,任何非0值的表示命令執行錯誤。
- $$:表示當前進程的進程號。
- $!:表示后台運行是最后一個進程的進程號。
- $0:表示當前執行進程的進程名。
預定義變量通常使用在腳本程序中,在命令行界面中的應用並不多見(盡管也可以使用)
11.4、 用戶自定義變量
1、定義新的變量
變量名=變量值
例:新建立一個名為“DAY”的變量,初始內容設置為“Sunday”。
# DAY=Sunday
2、查看和引用變量的值
通過在變量名稱前添加前導符號$,可以引用一個變量的內容。若需要在終端中輸出變量的內容,可以使用echo的命令,echo命令同時也用於顯示用戶指定的其他字符串內容。
例、查看變量DAY的內容,比較使用符號$與不使用符號$的區別。
當變量名稱容易和緊跟其后的其他字符相混淆時,需要添加大括號“{}”將其包圍起來,否則將無法確定正確的變量名稱。當末指定變量或該變量末設置時,命令將顯示空行。
例、在變量DAY的內容后緊跟“zhongxin”字符串並一起顯示。
3、為變量賦值的常用方法。
在符號“=”后邊直接指定變量內容是為變量賦值的最基本的方法,除此之外,管理員通常還會使用到其他的一些賦值勤操作,從而使變量內容的獲取更加靈活多變,以便適應於各種復雜的系統管理任務。常用的幾種變量賦值操作包括雙引號、單引號、后撇號、read命令。
1)雙引號(“)
使用雙引號時,允許在雙引號的范圍內使用$符號來引用其他變量的值(變量引用)。在簡單的賦值操作中,雙引號有時可以省略。
例、確認變量DAY的內容,並使用雙引號為TODAY變量賦值。
2)單引號(‘)
使用單引號時,將不允許在單引號的范圍內引用其他變量的值,$符號或者其他任何符號將作為普通字符看待。
例、確認變量DAY的內容,並使用單引號為TODAY變量賦值。
3)反撇號(`)
使用反撇號時,允許將執行特定命令的輸出結果賦給變量(命令替換),反撇號內包含的字串必須是能夠執行的命令,執行后會輸出結果替換該命令字串。新的可替代的語法是$( )。
例、統計當前登錄到本地終端中的用戶數量,並將結果保存到變量中。
# UserNum=$(w | grep” tty” | wc -l)
例、用一行命令找出安裝了fdisk程序的軟件包名稱(需要先確定fdisk程序的文件位置)。
例、通過find命令找出系統中用戶hackli留下的文件或目錄,並使用rm命令將其刪除。
在需要嵌套使用命令替換操作時,反撇號將力所不能及,這時可以將反撇號用$()來代替。
例、還是使用一行命令,將上一個例子的輸出結果保存到變量FdiskPKG中。
4)read命令
除了上述賦值操作外,還可以使用的Bash內置命令read來給變量賦值。read命令可以從終端(鍵盤)讀取輸入,實現簡單的交互過程。將從標准輸入讀入一行內容,並以空格為分隔符,將讀入的各字段分別賦值給指定列表中的變量(多余的內容賦值給最后一個變量)。若指定的變量只有一個,則將整行內容賦值給該變量。
例、從鍵盤輸入一整行數據,賦值給變量HELO,並確認HELO變量的內容。
例、從鍵盤輸入一整行數據,依次賦值給變量G1、G2,並確認G1、G2變量的內容。
為了使交互式操作的界面更加友好,提高易用性,命令可以結合“-p”選項來設置提示信息,用於告知用戶應該輸入的內容等相關事項。
例、從鍵盤讀出一個數值變量,並給出相應的提示信息。
4、設置變量的作用范圍
對於用戶自行定義的變量,默認情況下只能在當前的Shell環境中使用,因此稱為局部變量。局部變量在新開啟的子Shell環境中是無效的(無法引用定義的變量)。
例、在當前Shell環境中定義一個變量FILESVR,開啟一個新的Shell子進程,處於子Shell環境中時將無法使用變量FILESVR的內容。
為了使用戶定義的變量在所有的子Shell環境中能夠繼續使用,減少重復設置工作,可以使用export命令將指定的變量設置為“全局變量”。export命令可以同時使用多個變量名作為參數(不需要使用$符號),變量之間以空格分隔。
例、確認變量FILESVR的內容,並將其設置為全局變量,在子Shell、當前Shell環境中進行驗證。
export命令還可以在輸出變量的同時對指定名稱的變量進行賦值(創建),這樣在使用export命令之前就不需要單獨為變量進行賦值了。
例、定義兩個變量MONTH YEAR,並將其設置為全局變量。
5、清除自定義變量
當用戶不再需要使用自定義變量時,可以使用unset命令對已定義的用戶變量進行清除,指定一個或多個變量名稱作為參數即可(以空格分隔)
例、清除已設置的變量DAY、MONTH、YEAR。
6、數值變量的運算
Bash程序並不適合進行強大的數字運算(如小數、指數等),一般只適合進行簡單的整數運算。對Shell變量進行數值運算時,更多的時候是用於腳本程序的過程控制(循環次數等,下一章將會講解)。對整數數值變量進行算術運算時,可以使用expr表達式命令,格式如下。
Expr 變量1 運算符 變量2 [運算符 變量3] …..
或
$[ 變量1 運算符 變量2 ]
其中,變量1、變量2……對應為需要計算的數值變量(需要用$符號引用),常用的幾種運算符如下。
- “+”:加法運算
- “-”:減法運算
- “\*”:乘法運算,注意不能僅使用“*”符號,否則將被當成文件通配符
- “/”:除法運算
- “%”:求模運算,又稱為取模運算,即計算數值相除后的余數。
例、設置變量X、Y的值分別為34、12,依次計算變量X、Y的加減乘除及取模運算結果。
# echo $[x+y]
例、計算變量X的值與數值“123”的和,並將計算機結果重新賦值給變量
第十二章 BASH腳本(二)
12.1 Shell腳本的概念
Bash程序不僅可以作為用戶Linux管理系統命令的命令操作環境,同時也可以作為一種優秀的腳本程序語言。凡是使用Shell編程語言編寫的程序文件都可以稱為腳本,通俗一點的說,只要將一些命令行按順序保存到一個文本文件中,並給予這個文件可執行權限,那么這個文件就可以稱為Shell腳本。當然,Shell腳本是為了完成一定的管理任務才創建的,因此Shell腳本文件中各條命令並不是雜亂無章隨便放置的,這就需要用戶來進行組織和設計了。
寫編譯型的高級編程語言(如C/C++、Java等)不同,Shell腳本程序是屬於解釋執行的,並不需要進行特別編譯,而只需要有相應的命令解釋器即可。長期以來,Shell腳本在Linux系統中得到了廣泛的使用,並承擔着重要的角色。
12.2 編寫Shell腳本文件
1、建立包含可執行語句的文本文件
例、使用編輯器vi編寫一個簡單的腳本文件repboot.sh,用於報告當前系統中目錄所占有的空間大小,並列出其中內核文件的屬性信息。
在上述腳本文件中,依次設置了四條可執行命令,兩條echo語句用於顯示相應的提示信息,而du、ls命令分別用於完成查看目錄空間、顯示文件屬性任務。
2、為腳本文件添加可執行權限
編寫並保存好腳本文件后,需要執行該程序才能看到操作結果。但是剛建立的腳本文件通常不具有可執行屬性,因此還得使用命令為文件添加“X”權限。
例、為上一步編寫的腳本文件添加可執行權限。
將腳本文件設置了可執行屬性后就可以通過執行腳本文件來驗證程序的正確性了。
12.3執行腳本
在命令行環境中可以有多種方式執行腳本,下面分別進行介紹。
1、直接執行帶“X”權限的腳本文件
為腳本文件設置了可執行屬性后,在命令千可以直接通過腳本文件的路徑執行腳本程序,這也是最常用的一種方式。
例、執行當前目錄下的repboot.sh腳本程序文件。
由於腳本文件存放在當前目錄,而不是存放在用戶的命令搜索路徑,因此執行腳本文件時,需要在文件之前加入路徑,明確指出需要執行當前目錄下的腳本文件,這種方法也是出於對系統安全性的考慮。
2、使用解釋器程序執行腳本
這種方式可以將腳本文件作為指Shell定解釋器程序(如bash等)的參數,由解釋器程序負責讀取腳本文件中的內容並執行,因此並不需要腳本文件具有可執行的屬性。此方法通常只在腳本的調試階段使用。
例、使用解釋器程序手動加載執行腳本文件中的語句。
12.4 Shell腳本應用實例
本小節主要通過兩個編寫Shell管理腳本的具體實例,來展示普通Shell腳本的編寫應用。
1、Shell腳本案例1
由於公司的文件服務器空間有限,需要完成一項定期任務,即在每周五下班前(17:30)檢查公共共享目錄 /var/ftp/pub 中的內容,並將其中所有子目錄及文件的詳細列表和當時的時間信息追加保存到 /var/log/pubdir.log 日志文件中,然后清空該目錄中的內容。
根據上述需求描述,推薦的操作步驟如下。
(1)編寫腳本文件,並添加執行權限。
(2)設置計划任務並確認服務已經啟動。
2、腳本案例2
公司內網開發服務器中的數據庫目錄位於 /var/lib/mysql ,根據數據安全管理要求,至少每隔三天要做一次完整備份,備份前需要統計該目錄占用的總空間大小,並將備分日期、目錄空間大小等信息保存到臨時文件 /tmp/dbinfo.txt中,然后使用tar命令將dbinfo.txt文件隨數據庫目錄一起備份到 /opt/dbbak 目錄中,備份包文件名中要求體現當天的日期。
根據上述需求描述,推薦的操步驟如下。
(1)創建保存備份文件的目錄
(2)編寫腳本文件,並添加執行權限
(3)設置計划任務並確認服務已經啟動
12.5 Shell腳本結構控制語句
Shell作為一種解釋型腳本編程語言,同樣包含順序、分支、循環這些基本的程序控制結構。通過使用分支、循環等控制語句,可以編寫出應用更加靈活、功能更強大的Shell管理腳本。
12.5.1 使用if 條件語句
在Shell腳本中使用if語句的好處是:可以根據特定的條件來決定是否執行某項操作,當滿足不同條件執行不同的操作。本小節分別從條作測試操作、if語句結構、應用實例三個方面來講解if語句在Shell腳本的應用。
1、條件測試操作
需要在Shell腳本中有選擇的性的執行任務時,首先面臨的問題就是,如何設置命令執行的條件?
在Shell環境中,可以根據命令執行后的返回狀態值來判斷該命令是否成功執行,當返回值為0時表示成功執行,否則(非0值)表示執行失敗。用於特定條件表達示的測試時,可以使用Liunx系統中提供的專用工具——test命令。
使用test測試命令時,可以有以下兩種形式。
Test 條件表達式
或者
[ 條件表達式 ]
這兩種方式的作用完全相同,但通常后一種方式更為常用,也更貼近編程習慣。需要注意的是,方括號“[”或者“]與條件表達示語句之間至少需要一個空格進行分隔。
根據需要判斷的條件內容不同,條件操作也不同,最常用的條件操作主要包括文件狀態測試、比較整數值大小、比較字符串,以及同時判斷多個條件時的邏輯關系,下面將分別進行講解。在后續內容中,主要采用方括號的測試形式,不再對兩種形式進行對比。
1)測試文件狀態
文件狀態測試是指根據給定的路徑名稱,判斷該名稱對就的是文件還是目錄,或者判斷文件是否可讀、可寫、可執行等。根據所判斷的狀態不同,在條件表達式中需要使用不同的操作選項。
- -d:測試是否為目錄
- -e:測試目錄或文件是否存在
- -f:測試是否為文件
- -r:測試當前用戶是否有權限讀取
- -w:測試當前用戶是否有權限寫入
- -x:測試當前用戶是否可執行該文件
- -L:測試是否為符號連接文件
執行條件測試操作以后,通過預定義變量$?可以獲得測試命令的返回狀態值,從而能夠判斷該條件是否成立(返回0值表示條件成立,非0值表示條件不成立)但通過這種方式查看測試結果會比較繁瑣。
例、先后測試/etc、/etc/hosts是否是目錄,並通過$?變量查看返回狀態值,據此判斷測試結果。
雖然通過查看$?變量的值也可以判斷條件測試結果,但是操作比較繁瑣,輸出結果也不是很直觀。為了更便於查找看條件測試操作的結果,可以結合命令分隔符號&&和命令echo一起使用,當條件成立時直接輸出“YES“其中符號&&表示”而且“的關系,只有當前邊的命令執行成功后,才會執行后面的命令,否測后邊的命令將會被忽略。
例、測試 /media/cdrom及其父目錄是否存在,如果存在就顯示“YES”,否則不輸出任何信息。
例:使用teacher用戶登錄,並測試是否對 /etc/passwd 文件有讀、寫權限,如果有則顯示“YES”。
需要注意的是測試讀取、寫入權限時,盡量不要以root用戶的身份進行操作,否則可以會看不到預期的效果(因為root是超級管理員,許多文件權限都不受限制)
2) 整數值比較
整數值比較是指根據給定的兩個整數值,判斷第一個數是否大於、等於、小於……第2個數,可以使用的操作選項如下:
- -eq:第一個數等於第二個數
- -ne:第一個數不等於第二個數
- -gt:第一個數大於第二個數
- -lt:第一個數小於第二個數
- -le:第一個數小於或等到於第二個數
- -ge:第一個數大於或等於第二個數
整數值比較的測試操作在Shell腳本編寫中的應用較多,例如,用於判斷磁盤使用率,登錄用戶數量是否超標以及用於控制腳本語句的循環次數等。
例、測試當前登錄到系統中的用戶數量是否小於或等於10,是則輸出“YES”。
例、提取出/boot分區的磁盤使用率,並判斷是否超過95%(為了便於理解,操作步驟已適當進行分解)。
3) 字符串比較
字符串比較可以用於檢查用戶輸入,例如,在提供交互式操作時,判斷用戶輸入的選擇項是否與指定的變量內容相匹配。“=”、“!=”操作選項分別表示匹配、不匹配,“-z”操作選項用於檢查字符串是否為空。其中“!”符號用於取反,表示相反的意思。
例:提示用戶輸入一個文件路徑,並判斷是否是 /etc/inittab ,如果是則顯示“YES”。
例、若當前環境變量LANG的內容不是“en.US”,則輸出LANG變量的值,否則無輸出。
例、使用touch命令建立一個新文件test.txt,測試其內容是否為空,向文件中寫入內容后,再次進行測試。
4) 邏輯測試
邏輯測試是指同時使用的兩個(或多個)條件表達式之間的關系。用戶可以同時測試多個條件,根據這些條件是否同時成立或者只要有其中一個條件成立等情況,來決定采取何種操作。邏輯測試可以使用的操作選項如下。
- &&:邏輯與,表示前后兩個表達式都成立時整個測試結果才為真,否則結果為假。在使用test命令形式進行測試時,此選項可改為“-a”。
- ||:邏輯或,表示前后兩個條件至少有一個成立時整個測試結果才為真,否測結果為假。在使用命令形式進行測試時,此選項可改為“-o”
- !:邏輯否,表示當前指定的條件表達式不成立時,整個測試命令的結果為真。
在上述邏輯測試的操作選項中,“&&”和“||”和通常也用於間隔不同的命令操作,其作用是相似的(前面已經接觸過&&符號的應用)。同時使用多個邏輯運算操作時,一般按時從左到右的順序進行測試。
例、測試當前的用戶是否是teacher,若不是則提示 “Not teacher”。
例、只要 /etc/rc.d/rc.local 或者 /etc/init.d/rc.local中有一個是文件,則顯示“YES”,否則無任何輸出。
例、測試 /etc/profile 文件是否有可以執行權限,若確實沒有可執行權限,則提示 No x mode信息。
例、若當前的用戶是root且使用的Shell程序是 /bash/bash,則顯示“YES”否則無任何輸出。
2、if語句的結構
上一小節中學習了常用的一些條件測試操作,實際上使用和&&和||邏輯測試也可以完成簡單的判斷並執行相應的操作,但是當需要選擇執行的命令語句較多時,再使用這種方式將使命令行語句顯得很復雜,難以閱讀。而使用if語句,則可以更好的體現有選擇性執行的程序結構,使得層次分明,清晰易懂。
語句的選擇結構是由易到難可以分為三種類型,分別適用於不同的各種場合。
1) 單分支的語句
單分支的if語句是最簡單的選擇結構,這種結構只判斷指定的條件,當條件成立時執行相應的操作,否則不做任何操作。單分支使用的語句格式如下。
if 條件測試命令 ; then
命令序列
fi
在上述語句中,首先通過if判斷條件測試命令的返回狀態值是否為0(條件成立)如果是,則執行后面的一條或多條可執行語句(命令序列)一直到為fi表示結束;如果條件測試命令在返回狀態值不為0(條件不成立),則直接去執行后面的語句(如圖12-1所示)。
如圖12-1 單分支的if語句結構
在上述格式中,then關鍵字可以與if寫在一行中,其間通過分號“;”進行分隔即可,例如,使用if條件測試命令:then的形式。在Shell環境中,分號“;”也可以作為多條命令的分隔符,使用分號分隔的命令將按照順序依次被執行。
2)雙分支的語句
雙分支的if語句使用了兩路命令操作,在“條件成立”、“條件不成立”時分別執行不同的命令序列。雙分支使用的語句格式如下。
if 條件測試命令
then
命令序列1
else
命令序列2
fi
在上述語句中,首先通過判斷條件測試命令的返回狀態值是否為0(條件成立),如果是,則執行后面的一條或多條可執行語句(命令序列1),然后跳轉至結束判斷;如果條件測試命令的返回狀態不為0(條件不成立),則執行else后面的語句,一直到fi表示結束(如圖12-2所示)
圖12-2 雙分支的if語句結構
3)多分支的語句
由於if語句可以根據條件測試命令的兩種狀態分別進行操作,所以能夠嵌套使用,進行多次判斷(例如,首先判斷某學生的得分是否及格,若及格則再次判斷是否高於90分……)多重支使用的語句格式如下。
if 條件測試命令1
then
命令序列1
elif 件測試命令2
then
命令序列2
else
命令序列3
fi
上面的語法格式中只嵌套了一個elif語句,實際上語句中可以嵌套多個elif語句,if語句的嵌套在編寫shell腳本時並不常用,因為多重嵌套容易使程序結構變得復雜。需要使用多重分支程序結構時,更多的是使用case語句來實現(將在后面小節中講解)。
使用多分支的語句結構時,會依次對多個條件進行測試,一旦條件不成立時立即退出選擇結構,否則將執行相應的命令序列后再跳轉至fi,結束判斷(如圖示12-3所示)。
圖12-3 多分支的if語句結構
3、if語句應用系列
if語句是在Shell腳本中使用最多的一種程序結構,本小節將通過腳本實例的方式來展示if選擇結構的具體用法。
例、創建腳本chklog.sh,檢查文件 /var/log/messages 是否存在,若存在則統計文件內容的行數並輸出,否則不做任何操作(合理使用變量,可以提高編寫效率)。
例、創建腳本 mkbak.sh,提示用戶指定備份目錄的路徑,若目錄已存在則顯示提示信息后跳過,否則顯示相應提示信息后創建該目錄。
例、創建chkuser.sh腳本,統計當前登錄到系統中的用戶數量,並判斷是否超過三個,若是則顯示實際數量並給出警告信息,否則列出登錄的用戶賬號名稱用所在終端。
例、創建腳本chpostfix.sh,檢查postfix進程是否已經存在,若已經存在則輸出postfix service is running.;否則檢查是否存在 /etc/rc.d/init.d/postfix 可執行腳本,存在則啟動postfix服務,否則提示 no postfix script file。
例、編寫腳本chkmailsrv.sh每隔五分鍾監測一次postfix服務進程的運行狀態,若發現進程已終止,則在 /var/log/messages 文件中追加寫入日志信息(包括當時時間),並重啟postfix服務,否則不進行任何操作。
例:.創建一個shell腳本 /root/program:
--當你輸入“kernel”參數時,此shell腳本就返回“user“參數
--當你輸入”user“參數時,此shell腳本就返回”kernel”
--當此腳本不輸入任何參數或者輸入的參數是按照要求輸入的話,那么就會輸出標准錯誤“usage:/root/program kernel|user”。
(1).vim /root/program
#!/bin/bash
if [ $# -ne 1 ];then
echo "useage:/root/program kernel|user."
elif [ $1 = "user" ] ;then
echo "kernel"
elif [ $1 = "kernel" ] ;then
echo "user"
else
echo "useage:/root/program kernel|user."
fi
12.5.2 使用for循環語句
在腳本中使用for循環語句時,可以為變量設置一個取值列表,每次讀取列表中不同的變量值並執行相關命令操作,變量值用完以后則退出循環。Shell中的for語句不需要執行條件判斷,其使用變量的取值來自於預先設置的列表。
1、語句的結構
循環的語句格式如下。
for 變量名 in 取值列表
do
命令序列
done
在上述語句中,使用in關鍵字為用戶自定義變量設置了一個取值列表(以空格分隔的多個值),for語句第一次執行時首先將列表中的第一個取值賦給該變量,然后執行do后邊的命令序列;然后 再將列表中的第二個取值賦給該變量,然后執行do后邊的命令序列……如此循環,直到取值列表中的所有值都已經用完,最后跳至done語句,表示結束循環(如圖12-4所示)。
圖12-4 for循環語句的結構
2、for語句應用示例
for語句可以用於對一些特定列表值的處理(如過濾搜索到的文件),本小節將通過腳本實例的方式來展示for循環語句結構的具體方法。
例、依次輸出三條文字信息,包括一天中的“Morning”、“Noon”、“Evening”字串。
例、對於使用/bin/bash作為登錄Shell的系統用戶,檢查他們在 /opt 目錄中擁有的子目錄或文件數量,如果超過100個則列出具體數值及對應的用戶帳號。
例、計算 /etc 目錄中所有 *.conf 的形式的配置文件所占用的總空間大小。
12.5.3 使用while循環語句
在Shell腳本中使用while循環語句時,將可以根據特定的條件重復執行一個命令列表,直到該條件不再滿足時為止。除非有特別需要,否則在腳本程序中應該避免出現無限循環執行命令的情況,因為若無法跳出循環的話,后邊的命令操作將無法執行。為了控制循環次數,通常會在執行的命令序列中包含修改測試條件的語句,當循環達到一定次數后,測試條件將不再成立,從而可以結束循環。
1、 while語句的結構
循環的語句格式如下。
while 條件測試命令
do
命令序列
done
在上述語句中,首先通過while判斷條件測試命令的返回狀態值是否為0(條件成立),如果是則執行do后邊的命令序列,然后返回while再次進行條件測試並判斷返回狀態值,如果條件仍然成立,則繼續執行do后邊的命令序列,然后返回到while重復條件測試……如此循環,直到所測試的條件不成立時,跳轉到done語句,表示結束循環。(如圖12-5所示)。
圖12-5 while循環語句的結構
使用while循環語句時,有幾代人上特殊的條件測試返回值,即true(真)、false(假)。使用true作為測試條件時,條件將永遠成立,循環體現內的語句將不會被執行。這兩個特殊值也可以用在if語句的條件測試中。
2、 while語句應用實例
while語句可以用於需要重復操作的循環系統管理任務,並能夠通過設置循環條件來靈活實現各種管理任務。本小節將通過腳本實例的方式來展示循環結構的具體用法。
例、由用戶從鍵盤輸入一個大於1的整數(如50),並計算從1到該數之間各整數的和。
例、批量添加20個系統用戶帳號,用戶名稱依次為stu1、stu2、stu3…..,各用戶的初始密碼均設置為“123456”。
例、編寫一個批量刪除用戶的腳本程序,將上列中添加的20個用戶刪 除。
12.5.4其他控制語句
學會條件測試操作以及if、for、while語句的使用以后,編寫一般的系統管理任務腳本已經基本可以勝任了。當然,這其中需要大家將之前學習過的各種命令、管道、重定向等操作融會貫通,才能編寫出更有效的腳本程序。實際應用中的腳本程序靈活多變,即使是完成同一項任務,也可能有許多種不同的實現方式,大家應該勤加練習,在實踐過程中,慢慢去領略。
本節中將簡單介紹幾個其他的控制語句,以便在編寫腳本程序的過程中可以有更多的選擇。主要包括case分支語句、until循環、shift移位以及break和continue循環中斷語句。
1、 case語句
case語句適用於需要進行多重分支的應用情況,前面使用的多分支if語句,通常都可以修改為case語句,這樣程序結構將會更加清晰。
分支語句的格式如下。
Case 變量值
模式1)
命令序列1
;;
模式2)
命令序列2
;;
……….
*)
默認執行的命令的序列
Esac
在上述語句中,將使用case后邊的“變量值”與模式1、模式2、……等進行逐一比較(各模式中為用戶預設的固定值)直到找到與之相匹配的值,然后執行該模式下的命令序列,當遇到雙分號后跳至esac,表示結束分支。如果一直不到與之相匹配的值,則執行最后一個模式*后的命令序列,直到遇到esac后結束分支。(如圖12-6)。
圖12-6 case分支語句結構
case語句的結構特點
- case行尾必須為單詞in,每一模式必須以右括號“)”結束。
- 雙分號;;表示命令序列的結束。
- 匹配模式中可以使用方括號表示一個連續的范圍,例如[0—9];使用豎杠符號表示或,例如“A|B”(A或者B)。
- 最后的*表示默認模式,當使用前面的各種模式均無法匹配該變量時,將執行*后的命令序列。
例、由用戶從鍵盤輸入一個字符,並判斷該字符是否為字母、數字或者其他字符,並輸出相應的提示信息。
在Linux系統中很多腳本文件都使用了case分支語句,其中 /etc/rc.d/init.d 目錄下的各種系統服務腳本最為典型。有興趣的同學可以通過這些腳本來學習case語句的使用。例如在crond服務腳本的case語句結構中,用於匹配的變量為$1,也就是用戶輸入的第一個參數。當用戶執行 /etc/rc.d/init.d/crond start 命令時,$1的值就是start,因此腳本程序會執行start)模式后邊的命令序列,用於啟動crond服務。
例:.創建一個shell腳本 /root/program:
--當你輸入“kernel”參數時,此shell腳本就返回“user“參數
--當你輸入”user“參數時,此shell腳本就返回”kernel”
--當此腳本不輸入任何參數或者輸入的參數是按照要求輸入的話,那么就會輸出標准錯誤“usage:/root/program kernel|user”。
(1).vim /root/program
#!/bin/bash
case $1 in
kernel)
echo user
;;
user)
echo kernel
;;
*)
echo "usage:/root/program kernel|user."
;;
esac
2、 until 循環
until循環語句與while循環語句的結構非常類似,同樣是根據特定的條件決定是否執行循環體中的命令序列,只不過while循環語句是當測試條件成立時執行,而until循環是當測試條件成立時結束循環,循環語句的格式如下。
until 條件測試命令
do
命令序列
done
until的含義是“直到……為止”,因此同樣會首先執行條件測試並判斷其返回值,若條件不成立則執行循環,一直到該測試條件成立時為止,即退出循環.。(如圖12-7)
圖12-7 until循環語句的結構
大多數時候,通過將測試條件反置,while和until語句可以互相交換。
3、shift語句
shift實際上是Bash中一個特殊的內部命令,但是在命令行較少時用,而更多的是用在Shell腳本程序中,執行shift命令后,位置變量中($1~$9)的命令行參數會依次向左傳遞。例如:
若當前腳本程序獲得的位置變量如下。
$1=file1、$2=file2、$3=file3、$4=file4
則執行一次shift命令操作后(丟棄最左邊的一個值),各位置變量的值將變為如下所示。
$1=file2、$2=file3、$3=file4
再次shift執行命令操作后,各位置變量的值將變為如下所示.
$1=file3、$2=file4
合理利用shift語句,可以在位置參數的數量不固定的情況靈活實現程序的功能。
例、編寫一個程序,計算多個整數值的和,需要計算的各個數值由用戶在執行腳本時作為命令行參數給出。
4、循環控制語句
在使用for、while或until循環語句以及case分支語句的過程中,當滿足特定條件時可能會需要中斷循環體的執行,或者需要直接轉到開關重新判斷測試條件(而不再執行后邊的命令序列)。這時候,可以使用break和continue語句對執行流程進行控制,這兩個語句在與其他大部分編程語言中的含義是類似的。
1) break命令
break即中斷的意思,用於跳出當前據的循環體,但是並不退出程序。
例、循環提示用戶輸入字符串,並將每次輸入的內容保存到臨時文件 /tmp/input.txt 中,當用戶輸入“END”字符串時退出循環體,並統計出 input.txt文件中的行數、單詞數、字節數等信息,統計完后刪除臨時文件。
2) continue命令
continue即繼續的意思,用於暫停本次循環,跳轉至循環語句的頂部重新測試條件。本次執行過程中continue后的命令序列將被忽略。
例、刪除系統中的stu1~stu20各用戶帳號,但是stu8、stu18除外。
12.6 監控系統腳本例子
例:監控系統硬盤、內存、CPU、登錄用戶數,超出警戒值則告警。
# /scripts/sys-warning.sh
#監控系統硬盤、內存、CPU、登錄用戶數,超出警戒值則告警。
#!/bin/bash
#提取系統日期和時間
date=`date`
#提取本服務器的IP地址信息
IP=`ip add show eth0 | awk 'NR==3{print $2}'`
# 1、監控硬盤分區,當使用超過85%的時候發告警:(以下以boot分區為例)
#提取/分區(/dev/sda1)已用的百份比值(只取整數部分)
disk_root=`df -h |awk 'NR==2{print $5}'|awk -F % '{print $1}'`
#設置剩余硬盤容量的告警值為85%,如果當前硬盤使用超過85%,立即告警
if [ $disk_root -gt 85 ]; then
echo "$date,$IP 服務器根分區使用率已經超過85%,請及時處理。" >> /opt/warning.log
echo "$date,$IP 服務器 根分區 使用率已經超過85%,請及時處理。" | mail -s "$IP 服務器硬盤告警" root@example.com
fi
# 2、監控系統cpu的情況,當使用超過80%的時候發告警:
#取當前空閑cpu百份比值(只取整數部分)
cpu_idle=`top -bn 1 |awk NR==3'{print $8}'|cut -d . -f 1`
#設置空閑cpu的告警值為20%,如果當前cpu使用超過80%(即剩余小於20%),立即告警
if (($cpu_idle < 20)); then
echo "$date,$IP服務器cpu剩余$cpu_idle%,使用率超過80%,請及時處理。" >> /opt/warning.log
echo "$date,$IP服務器cpu剩余$cpu_idle%,使用率已經超過80%,請及時處理。" | mail -s "$IP 服務器CPU告警" root@example.com
fi
# 3、監控系統交換分區swap的情況,當使用超過80%的時候發告:
#系統分配的交換分區總量
swap_total=`free -m | grep Swap | awk '{print $2}'`
#當前剩余的交換分區free大小
swap_free=`free -m | grep Swap | awk '{print $4}'`
#當前已使用的交換分區used大小
swap_used=`free -m | grep Swap | awk '{print $3}'`
if (($swap_used != 0)); then
#如果交換分區已被使用,則計算當前剩余交換分區free所占總量的百分比,用小數來表示,要在小數點前面補一個整數位0
swap_per=0`echo "scale=2;$swap_free/$swap_total" | bc`
#設置交換分區的告警值為20%(即使用超過80%的時候告警)。
swap_warn=0.20
#當前剩余交換分區百分比與告警值進行比較(當大於告警值(即剩余20%以上)時會返回1,小於(即剩余不足20%)時會返回0 )
swap_now=`expr $swap_per \> $swap_warn`
#如果當前交換分區使用超過80%(即剩余小於20%,上面的返回值等於0),立即發郵件告警
if (($swap_now == 0)); then
echo "$date,$IP服務器swap分區只剩下 $swap_free M 未使用,剩余不足20%,使用率已經超過80%,請及時處理。" >> /opt/warning.log
echo "$date,$IP服務器swap分區只剩下 $swap_free M 未使用,剩余不足20%,使用率已經超過80%,請及時處理。" | mail -s "$IP 服務器內存告警" root@example.com
fi
fi
# 4、監控系統用戶登錄的情況,當用戶數超過3個的時候發告警郵件:
#取當前用戶登錄數(只取數值部分)
users=`uptime | awk '{print $4}'`
#設置登錄用戶數的告警值為3個,如果當前用戶數超過3個,立即發郵件告警
if (($users >= 3)); then
echo "$date,$IP 服務器用戶數已經達到$users個,請及時處理。" >> /opt/warning.log
echo "$date,$IP 服務器用戶數已經達到$users個,請及時處理。" | mail -s "$IP 服務器用戶數告警" root@example.com
fi
二、加入任務計划:每十分鍾檢測一次。
# chmod a+x /scripts/sys-warning.sh
# crontab -e
*/10 * * * * /scripts/sys-warning.sh
# service crond restart
第十三章 配置BASH環境
13.1 Shell的環境變量
這里所說的環境變量是指用戶登錄后Linux系統預先設好的一類Shell變量,其功能是設置用戶的Shell工作環境,包括用戶的宿主目錄、命令查找路徑、用戶當前的目錄、登錄的終端等。在實際使用過程中,環境變量並沒有嚴格的區分定義,用戶自己設置的變量也可以作為環境變量。
環境變量的名稱比較固定,通常使用大寫字母、數字和其他字符組成,而不使用小寫字母。環境變量的值一般由Linux系統自行維護,會隨着用戶狀態的改變而改變,用戶可以通過讀取環境變量來了解自己的當前狀態。
1、查看環境變量
通過set命令可以查看系統中所有Shell變量(包括環境變量和用戶自定義變量),由於內容輸出較多,建議使用less命令分頁查看。
常用環境變量:
PATH 決定了shell將到哪些目錄中尋找命令或程序
HOME 當前用戶主目錄
HISTSIZE 歷史記錄數
LOGNAME 當前用戶的登錄名
USER 當前用戶名
UID 當前用名的UID
HOSTNAME 指主機的名稱
SHELL 前用戶Shell類型
LANGUGE 語言相關的環境變量,多語言可以修改此環境變量
MAIL 當前用戶的郵件存放目錄
PS1 基本提示符,對於root用戶是#,對於普通用戶是$
PS2 附屬提示符,默認是“>”
例:以分號分隔,顯示當前的用戶的用戶名、宿主目錄、登錄Shell。
例:查看當前命令的搜索路徑,並將/opt/bin目錄添加到現有搜索路徑中去,從而可以直接執行此目錄中的命令。
2、環境變量的配置文件
用戶可在當前的Shell環境中直接為環境變量賦值,但需要長期變更所使用的某個環境變量時,可以修改配置文件。
在Linux系統中,用戶環境變量的設置工作習慣上在 /etc/profile 文件及宿主目錄中 .bash_profile文件中進行,前者稱為全局配置文件(對所有用戶起作用),后者稱為用戶配置文件(允許覆蓋全局配置)。
例:在當前用戶環境中,將用於限制歷史命令的環境變量HISTSIZE的值改為100。
例:編輯“~/.bash_profile”文件,修改PATH的設置,以便用戶在下次登錄后能夠使用服務/opt/bin 目錄作為默認的搜索路徑。
# vi /root/.bash_profiel
PATH=$PATH:$HOME/bin:/opt/bin
Export PATH
注意:登錄的幾個腳本文件
/etc/profile
/etc/bashrc
~/.bash_profile
~/.bashrc
~/.bash_logout
每一個文件都有特殊的功用並對登陸和交互環境有不同的影響。
/etc/profile 和 ~/.bash_profile 是在啟動 一個交互登陸shell的時候被調用。
/etc/bashrc 和 ~/.bashrc 是在一個交互的非登陸shell啟動的時候被調用。
~/.bash_logout 在用戶注銷登陸的時候 被讀取
一 個交互的登陸shell會在 /bin/login 成功登陸之后運行。一個交互的非登陸shell是通過命令行來運行的,如[prompt]$/bin/bash。一般一個非交互的shell出現在運行 shell腳本的時候 。之所以叫非交互的shell,是因為它不在命令行上等待輸入而只是執行腳本 程序。
3、命令別名
Bash中的命令別名功能可以將頻繁使用的復雜命令定義為簡短的別名,當用戶需要執行該條復雜命令時,只需要使用別名即可完成對應的操作,降低了操作復雜性,提高了輸入較率,別名功能主要通過這兩個命令進行設置和取消。
1) 查看已設置的別名
直接執行alias命令可以查看當前用戶環境中已設置的所有命令別名,若只需要查看待定的命令別名,可以使用別名名稱作為參數。
在系統中默認情況下已經定義好了幾個別名,例如,ll是ls -l命令的別名,這樣用戶在需要以長格式顯示文件信息時,就不需要每次都輸入ls -l命令,直接輸入ll命令即可。
2) 設置命令別名
用戶可以根據自己的使用習慣定義特定的命令別名,具體格式可參考alias命令的輸出結果,基本的形式為:alias別名=’實際命令’
3) 取消已設置的命令別名
需要取消系統中定義的命令別名時,可以使用unalias命令。使用別名名稱作為參數可以刪除特定的命令別名,結合-a選項使用時將刪除所有已定義的命令別名。
使用alias命令定義新的別名或使用unalias命令取消已定義的別名都只在用戶的當前Shell環境中有效,下次啟動后將恢復初始的別名設置。因此對於用戶經常使用的命令別名,應該將相應的別名設置命令添加到宿主目錄的 .bashrc文件中。
在系統server0和desktop0上創建自定義命令為psa,
此自定義命令將執行/bin/ps -aux,
此命令對系統中所有用戶有效。
[root@server0 ~]# Vim /etc/bashrc
alias psa='/bin/ps -aux'
[root@server0 ~]# srource /etc/bashrc