系統管理中 bash shell 腳本常用方法總結
在日常系統管理工作中,需要編寫腳本來完成特定的功能,編寫shell腳本是一個基本功了!在編寫的過程中,掌握一些常用的技巧和語法就可以完成大部分功能了,也就是2/8原則.
1. 單引號和雙引號的區別單引號與雙引號的最大不同在於雙引號仍然可以引用變數的內容,但單引號內僅是 普通字元 ,不會作變數的引用,直接輸出字元竄。請看如下例子:
[root@linux ~]# name=HaHa[root@linux ~]# echo $nameHaHa[root@linux ~]# myname="$name is wow"[root@linux ~]# echo $mynameHaHa is wow[root@linux ~]# myname="$name is wow"[root@linux ~]# echo $myname$name is wow
從上面例子可以看出,使用了單引號的時候,那麼$name只是普通字元,直接輸出而已!
2. 逐行讀取文件
for line in `cat file.txt` do echo $line done
注意:由於使用for來讀入文件里的行時,會自動把空格和換行符作為一樣分隔符,如果行里有空格的時候,輸出的結果會很亂,所以只適用於行連續不能有空格或者換行符的文件
cat file.txt |while read line do echo $line done 或者: while read line do echo $line done < file.txt
注意:由於使用while來讀入文件里的行時,會整行讀入,不會關注行的內容(空格..),所以比for讀文件有更好的適用性,推薦使用while循環讀取文件
3. bash shell 腳本中常用隱含變數
$0 | 當前執行的腳本或者命令名稱 |
$1-$9 | 代表參數的位置. 舉例 $1 代表第一個參數. |
$# | 腳本調用的參數的個數 |
$@ | 所有參數的內容 |
$* | 所有參數的內容 |
$$ | 當前運行腳本的進程號 |
$? | 命令執行後返回的狀態 |
$! | 後台運行的最後一個進程號 |
注意: $? 用於檢查上一個命令執行是否正確(在Linux中,命令退出狀態為0表示該命令正確執行,任何非0值表示命令出錯)$$ 變數最常見的用途是用做暫存文件的名字以保證暫存文件不會重複。$* 和 $@ 如果輸出是一樣的,但是在使用for循環,在使用 雙引號(」")引用時 「$*」 會輸出成一個元素 而 「$@」 會按照每個參數是一個元素方式輸出
請看測試例子
#cat test.sh#!/bin/shecho ""$@" output....."for i in "$@"doecho $idoneecho ""$*" output ...."for i in "$*"doecho $idone
輸出結果
#sh test.sh a b c d"$@" output.....abcd"$*" output ....a b c d
從輸出結果可以看出 「$*」 輸出是一行 而 「$@」 輸出則是四行
4. 變數內容的刪除與替換
我們在一些情況下,需要對變數中的字元竄進行查找刪除或者替換,就需要使用下表列出的方法
變數設定方式 | 說明 | |
---|---|---|
${變數#關鍵字} | 若變數內容從頭開始的資料符合『關鍵字』,則將符合的最短資料刪除 | |
${變數##關鍵字} | 若變數內容從頭開始的資料符合『關鍵字』,則將符合的最長資料刪除 | |
${變數%關鍵字} | 若變數內容從尾向前的資料符合『關鍵字』,則將符合的最短資料刪除 | |
${變數%%關鍵字} | 若變數內容從尾向前的資料符合『關鍵字』,則將符合的最長資料刪除 | |
${變數/舊字串/新字串} | 若變數內容符合『舊字串』則『第一個舊字串會被新字串取代 | |
${變數//舊字串/新字串} | 若變數內容符合『舊字串』則『全部的舊字串會被新字串取代 |
舉例如下(刪除字元竄中的某個字元):
[root@linux ~]# export test_str="/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"[root@linux ~]# echo ${test_str#/*kerberos/bin:}/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
5. 變數條件測試賦值
在某些時刻我們需要『判斷』某個變數是否存在,若變數存在則將此變數值賦值給新的變數,若變數不存在則將其他值賦值給新的變數.
變數設定方式 | str 未定義 | str 為空字串 | str 已賦值為非空字串 | |
---|---|---|---|---|
var=${str-expr} | var=expr | var= | var=$str | |
var=${str:-expr} | var=expr | var=expr | var=$str | |
var=${str+expr} | var= | var=expr | var=expr | |
var=${str:+expr} | var= | var= | var=expr | |
var=${str?expr} | expr 輸出至 stderr | var= | var=$str | |
var=${str:?expr} | expr 輸出至 stderr | expr 輸出至 stderr | var=$str | |
var=${str=expr} | var=expr | var= | var=$str | |
var=${str:=expr} | var=expr | var=expr | var=$str |
舉例如下:
[root@linux ~]# test_name=""[root@linux ~]# test_name=${test_name-root}[root@linux ~]# echo $test_name<== 因為 test_name 被設定為空字元竄!所以當然還是保留為空字元竄![root@linux ~]# test_name=${test_name:-root}[root@linux ~]# echo $test_nameroot <== 加上『:』後若變數內容為空或者是未設定,都能夠以後面的內容替換!
基本上這種變數的測試也能夠透過 shell script 內的 if…then… 來處理,不過通過上述提及的簡單的方法來測試變數,是程序看起來更精簡一些!
6. shell 中分隔符 : 變數IFS 使用
shell腳本中,如果使用for循環一個字元竄的話,默認使用空格來分割字元竄. 還有前面所提到的使用for循環逐行讀取文件內容時候,文件行中如果有空格的話輸出的結果也會變亂. 這個時候 使用 IFS 變數來設置特定的字元竄分割符來,達到輸出正確的目的. 默認情況下 IFS 是使用 <space><tab><newline>, 空格 "t "n 來作為默認的分割符的.
我們將前面使用for逐行讀取文件的例子 改進下就可以輸出正確了,請看下面
#!/bin/bashIFS_old=$IFS #將原IFS值保存,以便用完後恢復IFS=$』"n』 #更改IFS值為$』"n』for line in `cat file.txt`doecho $linedone
file.txt 文件內容如下
[root@linux]$ cat file.txtsdfsdfsdfsdfssssss ssssss ssssss ssssssdfsdfsdfsdfsdf
執行測試程序 輸出結果如下(正確輸出)
[root@linux]$ sh test.shsdfsdfsdfsdfssssss ssssss ssssss ssssssdfsdfsdfsdfsdf
如果未設置IFS變數,使用默認的IFS變數值 ,輸出結果如下
[root@linux]$ sh test.shsdfsdfsdfsdfssssssssssssssssssssssssdfsdfsdfsdfsdf
從以上測試程序輸出結果,可以根據自己的需求來設定 IFS變數,在舉一個例子如下:
while IFS=: read userName passWord userID groupID geCos homeDir userShelldoecho "$userName -> $homeDir"done < /etc/passwd
7. shell 數組的使用
數組賦值方式:
(1) array=(var1 var2 var3 ... varN)(2) array=([0]=var1 [1]=var2 [2]=var3 ... [n]=varN)(3) array[0]=var1arrya[1]=var2...array[n]=varN
計算數組元素個數或者長度:
(1) ${#array[@]}(2) ${#array[*]}
了解了數組基礎語法,舉例說明,請看:
#!/bin/bashNAMESERVERS=("ns1.www.net." "ns2.www.net." "ns3.www.net.")# 得到數組長度tLen=${#NAMESERVERS[@]}# 循環數組for (( i=0; i<${tLen}; i++ ));doecho ${NAMESERVERS[$i]}done
在看一個複雜一點的例子,將文件內容讀取到數組中:
#!/bin/bash# 設置IFS將分割符 設置為 換行符("n)OLDIFS=$IFSIFS=$""n" # 讀取文件內容到數組fileArray=($(cat file.txt))# restore itIFS=$OLDIFStLen=${#fileArray[@]}# 循環顯示文件內容for (( i=0; i<${tLen}; i++ ));doecho "${fileArray[$i]}"done
8. 邏輯判斷 條件測試
操作符 | 測試結果 | |
---|---|---|
-e filename | 文件存在返回1, 否則返回0 | |
-r filename | 文件可讀返回1,否則返回0 | |
-w filename | 文件可寫返回1,否則返回0 | |
-x filename | 文件可執行返回1,否則返回0 | |
-o filename | 文件屬於用戶本人返回1, 否則返回0 | |
-z filename | 文件長度為0返回1, 否則返回0 | |
-f filename | 文件為普通文件返回1, 否則返回0 | |
-d filename | 文件為目錄文件時返回1, 否則返回0 |
舉例如下,測試文件是否存在:
#!/bin/bashecho "checks the existence of the messages file."echo -n "Checking..."if [ -f /var/log/messages ];thenecho "/var/log/messages exists."fiechoecho "...done."
操作符 | 比較結果 | |
---|---|---|
str1 = str2 | 當兩個字串相等時為真 | |
str1 != str2 | 當兩個字串不等時為真 | |
-n str1 | 當字元串的長度大於0時為真 | |
-z str1 | 當字元串的長度為0時為真 | |
str | 當字元串為非空時為真 |
舉例如下,比較字元串來測試用戶ID :
if [ "$(whoami)" != "root" ]; thenecho "You have no permission to run $0 as non-root user."exit 1;fi
操作符 | 比較結果 | |
---|---|---|
num1 -eq num2 | 兩數相等為真 | |
num1 -ne num2 | 兩數不等為真 | |
num1 -gt num2 | num1大於num2為真 | |
num1 -ge num2 | num1大於等於num2為真 | |
num1 -lt num2 | num1小於num2為真 | |
num1 -le num2 | num1小於等於num2為真 |
舉例如下:
num=`wc -l work.txt`if [ $num -gt 150 ];thenecho "you"ve worked hard enough for today."echofi
如果要查看詳細的測試操作,可以查看man手冊 man test
推薦閱讀:
※天校2.0正式發布 更方便的系統管理工具
※使用 Ansible 讓你的系統管理自動化
※Ansible:像系統管理員一樣思考的自動化框架
※Ansible 教程:簡單 Ansible 命令介紹