標籤:
在日常系統管理工作中,需要編寫指令碼來完成特定的功能,編寫shell指令碼是一個基本功了!
在編寫的過程中,掌握一些常用的技巧和文法就可以完成大部分功能了,也就是2/8原則
1. 單引號和雙引號的區別
單引號與雙引號的最大不同在於雙引號仍然可以引用變數的內容,但單引號內僅是一般字元,不會作變數的引用,直接輸出字元竄。請看如下例子:
[[email protected] ~]# name=HaHa
[[email protected] ~]# echo $name
HaHa
[[email protected] ~]# myname="$name is wow"
[[email protected] ~]# echo $myname
HaHa is wow
[[email protected] ~]# myname=‘$name is wow‘
[[email protected] ~]# echo $myname
$name is wow
從上面例子可以看出,使用了單引號的時候,那麼$name只是一般字元,直接輸出而已!
2. 逐行讀取檔案
注意:由於使用for來讀入檔案裡的行時,會自動把空格和分行符號作為一樣分隔字元,如果行裡有空格的時候,輸出的結果會很亂,所以只適用於行連續不能有空格或者分行符號的檔案
注意:由於使用while來讀入檔案裡的行時,會整行讀入,不會關注行的內容(空格..),所以比for讀檔案有更好的適用性,推薦使用while迴圈讀取檔案
3. bash shell 指令碼中常用隱含變數
$0 |
當前執行的指令碼或者命令名稱 |
$1-$9 |
代表參數的位置. 舉例 $1 代表第一個參數. |
$# |
指令碼調用的參數的個數 |
[email protected] |
所有參數的內容 |
$* |
所有參數的內容 |
$$ |
當前運行指令碼的進程號 |
$? |
命令執行後返回的狀態 |
$! |
後台啟動並執行最後一個進程號 |
注意: $? 用於檢查上一個命令執行是否正確(在Linux中,命令退出狀態為0表示該命令正確執行,任何非0值表示命令出錯)
$$ 變數最常見的用途是用做暫存檔案的名字以保證暫存檔案不會重複。
$* 和 [email protected] 如果輸出是一樣的,但是在使用for迴圈,在使用 雙引號("")引用時 "$*" 會輸出成一個元素 而 "[email protected]" 會按照每個參數是一個元素方式輸出
請看測試例子
#cat test.sh
#!/bin/sh
echo ‘"[email protected]" output.....‘
for i in "[email protected]"
do
echo $i
done
echo ‘"$*" output ....‘
for i in "$*"
do
echo $i
done
輸出結果
#sh test.sh a b c d
"[email protected]" output.....
a
b
c
d
"$*" output ....
a b c d
從輸出結果可以看出 "$*" 輸出是一行 而 "[email protected]" 輸出則是四行
4. 變數內容的刪除與替換
我們在一些情況下,需要對變數中的字元竄進行尋找刪除或者替換,就需要使用下表列出的方法
變數設定方式 |
說明 |
${變數#關鍵字} |
若變數內容從頭開始的資料符合‘關鍵字’,則將符合的最短資料刪除 |
${變數##關鍵字} |
若變數內容從頭開始的資料符合‘關鍵字’,則將符合的最長資料刪除 |
${變數%關鍵字} |
若變數內容從尾向前的資料符合‘關鍵字’,則將符合的最短資料刪除 |
${變數%%關鍵字} |
若變數內容從尾向前的資料符合‘關鍵字’,則將符合的最長資料刪除 |
${變數/舊字串/新字串} |
若變數內容符合‘舊字串’則‘第一箇舊字串會被新字串取代 |
${變數//舊字串/新字串} |
若變數內容符合‘舊字串’則‘全部的舊字串會被新字串取代 |
舉例如下(刪除字元竄中的某個字元):
[[email protected] ~]# export test_str="/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
[[email protected] ~]# 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 |
舉例如下:
[[email protected] ~]# test_name=""
[[email protected] ~]# test_name=${test_name-root}
[[email protected] ~]# echo $test_name
<== 因為 test_name 被設定為空白字元竄!所以當然還是保留為空白字元竄!
[[email protected] ~]# test_name=${test_name:-root}
[[email protected] ~]# echo $test_name
root <== 加上‘:’後若變數內容為空白或者是未設定,都能夠以後面的內容替換!
基本上這種變數的測試也能夠透過 shell script 內的 if...then... 來處理,不過通過上述提及的簡單的方法來測試變數,是程式看起來更精簡一些!
6. shell 中分隔字元 : 變數IFS 使用
shell指令碼中,如果使用for迴圈一個字元竄的話,預設使用空格來分割字元竄.還有前面所提到的使用for迴圈逐行讀取檔案內容時候,檔案行中如果有空格的話輸出的結果也會變亂.這個時候使用 IFS 變數來設定特定的字元竄分割符來,達到輸出正確的目的.預設情況下 IFS 是使用 , 空格 \t \n 來作為預設的分割符的.
我們將前面使用for逐行讀取檔案的例子 改進下就可以輸出正確了,請看下面
#!/bin/bash
IFS_old=$IFS #將原IFS值儲存,以便用完後恢複
IFS=$’\n’ #更改IFS值為$’\n’
for line in `cat file.txt`
do
echo $line
done
file.txt 檔案內容如下
[[email protected]]$ cat file.txt
sdfsdfsdfsdf
ssssss ssssss ssssss sssss
sdfsdfsdfsdfsdf
執行測試程式 輸出結果如下(正確輸出)
[[email protected]]$ sh test.sh
sdfsdfsdfsdf
ssssss ssssss ssssss sssss
sdfsdfsdfsdfsdf
如果未設定IFS變數,使用預設的IFS變數值 ,輸出結果如下
[[email protected]]$ sh test.sh
sdfsdfsdfsdf
ssssss
ssssss
ssssss
sssss
sdfsdfsdfsdfsdf
從以上測試程式輸出結果,可以根據自己的需求來設定 IFS變數,在舉一個例子如下:
while IFS=: read userName passWord userID groupID geCos homeDir userShell
do
echo "$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]=var1
arrya[1]=var2
...
array[n]=varN
計算數組元素個數或者長度:
(1) ${#array[@]}
(2) ${#array[*]}
瞭解了數組基礎文法,舉例說明,請看:
#!/bin/bash
NAMESERVERS=("ns1.www.net." "ns2.www.net." "ns3.www.net.")
# 得到數組長度
tLen=${#NAMESERVERS[@]}
# 迴圈數組
for (( i=0; i<${tLen}; i++ ));
do
echo ${NAMESERVERS[$i]}
done
在看一個複雜一點的例子,將檔案內容讀取到數組中:
#!/bin/bash
# 設定IFS將分割符 設定為 分行符號(\n)
OLDIFS=$IFS
IFS=$‘\n‘
# 讀取檔案內容到數組
fileArray=($(cat file.txt))
# restore it
IFS=$OLDIFS
tLen=${#fileArray[@]}
# 迴圈顯示檔案內容
for (( i=0; i<${tLen}; i++ ));
do
echo "${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/bash
echo "checks the existence of the messages file."
echo -n "Checking..."
if [ -f /var/log/messages ];then
echo "/var/log/messages exists."
fi
echo
echo "...done."
操作符 |
比較結果 |
str1 = str2 |
當兩個字串相等時為真 |
str1 != str2 |
當兩個字串不等時為真 |
-n str1 |
當字串的長度大於0時為真 |
-z str1 |
當字串的長度為0時為真 |
str |
當字串為非空時為真 |
舉例如下,比較字串來測試使用者ID :
if [ "$(whoami)" != ‘root‘ ]; then
echo "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 ];then
echo "you‘ve worked hard enough for today."
echo
fi
如果要查看詳細的測試操作,可以查看man手冊 man test
系統管理中 bash shell 指令碼常用方法總結