============================== SHELL編程 ==============================
一、Shell語法
1.1、變量聲明
1.2、數組聲明和使用
1.3、特殊的變量
1.4、運算和運算符
1.4.1、整數運算符
1.5、流程語句
1.5.1、if 語句
1.5.2、case語句
1.5.3、select 語句
1.6、循環語句
1.6.1、for語句
1.6.2、while 語句
1.6.3、breake 和continue
1.6.4、shift
二、函數
三、shell程序調試
四、信號
五、bash中常用的命令
=============== 一、Shell語法 ===============
1.1、變量聲明
變量=值 (注意:等號兩側不能有空格)
a=”hello”
b=9
unset a 撤銷變量 a
readonly a=2 聲明靜態的變量 a=2 ,不能 unset
export 變量名 可把變量提升為全局環境變量,可供其他shell程序使用
變量應用中要注意:
echo “$1 = $1 ; $2 = $2 “; 屏蔽$1 ,屏蔽 $2 ,直接顯示 $1 ,$2,同理*也屏蔽了 * 的通配符作用
${SAT}day ,變量要與字符串連在一起時,應該用 ${}
a= `ls -al` 反引號,運行里面的命令,並把結果返回給變量a
a=$(ls -al) 等價於反引號
單引號‘’和雙引號“”的區別,單引號完全屏蔽 $a 變量,雙引號不屏蔽$a,單引號和雙引號都屏蔽 * 的通配符作用。
1.2、數組聲明和使用
1 name[0]="Tom" 2 name[1]="Tomy" 3 name[2]="John" 4 或 5 name=("Tom" "Tomy" "John")
例子:
1 #!/bin/bash 2 name=("Tom" "Tomy" "John") 3 for i in 0 1 2 4 do 5 echo $i:${name[$i]} 6 done
1.3、特殊的變量
$0:腳本名字。此變量包含地址,可以使用basename $0獲得腳本名稱。
$1:第一個參數
$2,$3,$4,$5,…一次類推。
$# 傳遞到腳本的參數個數
$* 以一個單字符串顯示所有向腳本傳遞的參數,,以(“$1 $2…”)
$$ 腳本運行的ID號
$! 后台運行的最后一個進程的ID號
$@ 與$*相同,但是使用時加引號,並在引號中返回每個參數。(“$1″”$2″…)
$- 顯示shell使用的當前選項。
$? 顯示最后命令的推出狀況。0表示沒有錯誤。
例子:
1 #!/bin/sh 2 if [ $# -ne 2 ] ; then 3 echo "Usage: $0 string file"; 4 exit 1; 5 fi 6 grep $1 $2 ; 7 if [ $? -ne 0 ] ; then 8 echo "Not Found "$1" in $2"; 9 exit 1; 10 fi 11 echo "Found "$1" in $2";
上面的例子中使用了$0 $1 $2 $# $? 等變量,下面是程序的解釋:
判斷運行參數個數,如果不等於2,顯示使用”用法幫助”, 其中 $0 表示就是腳本自己。
用grep 在$2 文件中查找$1 字符串。
判斷前一個命令運行后的返回值(一般成功都會返回0, 失敗都會返回非0)。
如果沒有成功顯示沒找到相關信息,否則顯示找到了。
其中”表示轉義,在”" 里面還需要顯示”號,則需要加上轉義符”
參數置換的變量
1、變量=${參數:-word}:如果設置了參數,則用參數的值置換變量的值,否則用word置換。即這種變量的值等於某一個參數的值,如果該參數沒有設置,則變量就等於word的值。
[ -z "${COLUMNS:-}" ] && COLUMNS=80
2、變量=${參數:=word}:如果設置了參數,則用參數的值置換變量的值,否則把變量設置成word,然后再用word替換參數的值。注意,位置參數不能用於這種方式,因為在Shell程序中不能為位置參數賦值。
3、變量=${參數:?word}:如果設置了參數,則用參數的值置換變量的值,否則就顯示word並從Shell中退出,如果省略了word,則顯示標准信息。這種變量要求一定等於某一個參數的值。如果該參數沒有設置,就顯示一個信息,然后退出,因此這種方式常用於出錯指示。
4、變量=${參數:+word}:如果設置了參數,則用word置換變量,否則不進行置換。
字符串匹配的操作:
${PARAMETER#WORD} shell 像文件名擴展中那樣擴展 WORD,並從 PARAMETER 擴展后的值的開頭刪除最短的匹配模式(若存在匹配模式的話)。使用 ‘@’ 或 ‘$’ 即可刪除列表中每個參數的模式。
${PARAMETER##WORD} 導致從開頭刪除最長的匹配模式而不是最短的匹配模式。
${PARAMETER%WORD} shell 像文件名擴展中那樣擴展 WORD,並從 PARAMETER 擴展后的值末尾刪除最短的匹配模式(若存在匹配模式的話)。使用 ‘@’ 或 ‘$’ 即可刪除列表中每個參數的模式。
${PARAMETER%%WORD} 導致從末尾刪除最長的匹配模式而不是最短的匹配模式。
${PARAMETER/PATTERN/STRING} shell 像文件名擴展中那樣擴展 PATTERN,並替換 PARAMETER 擴展后的值中最長的匹配模式(若存在匹配模式的話)。為了在 PARAMETER 擴展后的值開頭匹配模式,可以給 PATTERN 附上前綴 #,如果要在值末尾匹配模式,則附上前綴 %。如果 STRING 為空,則末尾的 / 可能被忽略,匹配將被刪除。使用 ‘@’ 或 ‘$’ 即可對列表中的每個參數進行模式替換。
${PARAMETER//PATTERN/STRING} 對所有的匹配(而不只是第一個匹配)執行替換。
變數設定方式 str 沒有設定 str 為空字串 str 已設定非為空字串
1 var=${str-expr} var=expr var= var=$str 2 var=${str:-expr} var=expr var=expr var=$str 3 var=${str+expr} var= var=expr var=expr 4 var=${str:+expr} var= var= var=expr 5 var=${str=expr} str=expr 6 var=expr str 不變 7 var= str 不變 8 var=$str 9 var=${str:=expr} str=expr 10 var=expr str=expr 11 var=expr str 不變 12 var=$str 13 var=${str?expr} expr 輸出至 stderr var= var=str 14 var=${str:?expr} expr 輸出至 stderr expr 輸出至 stderr var=str 15 16 17 [subsir@pinguino ~]$ x="a1 b1 c2 d2" 18 [subsir@pinguino ~]$ echo ${x#*1} 19 b1 c2 d2 20 [subsir@pinguino ~]$ echo ${x##*1} 21 c2 d2 22 [subsir@pinguino ~]$ echo ${x%1*} 23 a1 b 24 [subsir@pinguino ~]$ echo ${x%%1*} 25 a 26 [subsir@pinguino ~]$ echo ${x/1/3} 27 a3 b1 c2 d2 28 [subsir@pinguino ~]$ echo ${x//1/3} 29 a3 b3 c2 d2 30 [subsir@pinguino ~]$ echo ${x//?1/z3} 31 z3 z3 c2 d2
字符串子集提取:
${x:3:5}
的值就是 “e val”,
清單 9. shell 參數值的子字符串
1 [subsir@pinguino ~]$ x="some value" 2 [subsir@pinguino ~]$ echo "${x:3:5}" 3 e val
字符串長度:
您已經知道 $# 表示參數的數量,而 ${PARAMETER:OFFSET:LENGTH} 擴展適用於單個參數以及 $* 和 $@,因此,可以使用一個類似的結構體 ${#PARAMETER} 來確定單個參數的長度也就不足為奇了。清單 10 中所示的簡單的 testlength 函數闡明了這一點。自己去嘗試使用它吧。
清單 10. 參數長度
1 [subsir@pinguino ~]$ testlength () { for p in "$@"; do echo ${#p};done } 2 [subsir@pinguino ~]$ testlength 1 abc "def ghi" 3 1 4 3 5 7
1.4、運算和運算符
1.4.1、整數運算符
整數的算術運算符
+ - * / %
賦值運算符
+= -= * = / = %=
位運算符
<< >> & | ~ ^
位運算賦值運算符
<< = >> = & = | = ~ = ^ =
邏輯運算符:
&& || ! > > = < < = != ==
expr命令計算一個表達式的值
格式 :expr arg
例子:
計算(2 +3 )×4 的值
1 、分步計算,即先計算2 +3 ,再對其和乘4
s=`expr 2 + 3`
expr $s * 4
2 、一步完成計算:
expr `expr 2 + 3 ` * 4
說明:
運算符號和參數之間要有空格分開;
通配符號(* ), 在作為乘法運算符時要用 、“” 、‘’ 符號修飾
關鍵字let計算表達式的值:
#!/bin/bash
x=2006
let "x = $x + 1"
echo $x
x="a string."
echo $x
又出現了新的關鍵字:let。關於整數變量計算,有如下幾種:" + - * / % ",他們的意思和字面意思相同,在*和/之前必須冠以反斜線,已防被SHELL先行解釋。整數運算一般通過 let 和 expr 這兩個指令來實現,如對變量 x 加 1 可以寫作:let "x = $x + 1" 或者 x=`expr $x + 1`
1.4.2、邏輯運算符
對應操作 整數 字符串
相同 -eq =
不同 -ne !=
大於 -gt >
小於 -lt <
大於或等於 -ge
小於或等於 -le
為空 -z
不為空 -n
文件操作邏輯運算符:
-d file ----當file是一個目錄時,返回 True
-f file ----當file是一個普通文件時,返回 True
-r file ----當file是一個只讀文件時,返回 True
-s file ----當file文件長度大於0時,返回 True
-w file ----當file是一個可寫文件時,返回 True
-x "/bin/ls" ----當/bin/ls是一個可執行文件時,返回 True,目錄是否可訪問
-e file ----當file存在時,返回True
-o file ----當file文件的所有者是當前用戶時,返回True
-z file ----當file長度為0時,返回True
-u -----文件的 UID 標志被設置
-G -----文件的組 ID 和當前用戶相同
file1 -nt file2 -----文件 file1 比 file2 更新
file1 -ot file2 -----文件 file1 比 file2 更老
邏輯連接符:
! expr 當expr的值是False時,返回True
Expr1 -a expr2 當expr1,expr2值同為True時,返回True
Expr1 -o expr2 當expr1,expr2的值至少有一個為True時,返回True
命令邏輯連接符:
[ -r "$mailfolder" ]||{ echo "Can not read $mailfolder" ; exit 1; }
使用{}把兩個命令括起來,表示一個函數的用法。 && 與 ||或
[ -f "/etc/shadow" ] && echo "This computer uses shadow passwors"
注意:在“[”和“]”符號的左右都留有空格
例子:
1 #!/bin/sh 2 mailfolder=/var/spool/mail/james 3 [ -r "$mailfolder" ]||{ echo "Can not read $mailfolder" ; exit 1; } 4 echo "$mailfolder has mail from:" 5 grep "^From " $mailfolder 6 其中 “^From“ 表示以 From 開頭的
1.5、流程語句
1.5.1、if 語句
1 if [ 邏輯表達式 ]; then 2 #command code 3 elif [ 邏輯表達式 ]; then 4 #commandcode 5 else 6 #commandcode 7 fi
如果您為了簡潔,想把 then 和 if 放在一行,那就要這樣寫了:if [ expression ]; then。即在 then 前加一個“;”號。
1.5.2、case語句
case string1 in
str1 ) commands1;;
str2 ) commands2;;
*) commands3;;
esac
例子:
#file lf.gz
lf.gz: gzip compressed data, deflated, original filename,
last modified: Mon Aug 27 23:09:18 2001, os: Unix
腳本:
1 #!/bin/sh 2 ftype=`file "$1"` 3 case "$ftype" in 4 "$1: Zip archive"*) unzip "$1" ;; 5 "$1: gzip compressed"*) gunzip "$1" ;; 6 "$1: bzip2 compressed"*)bunzip2 "$1" ;; 7 *) # * 通配符 代表其他 8 error "File $1 can not be uncompressed with smartzip";; 9 esac
例子:
1 #!/bin/bash 2 echo "Hit a key, then hit return." 3 read Keypress 4 5 case "$Keypress" in 6 [a-z] ) echo "Lowercase letter";; 7 [A-Z] ) echo "Uppercase letter";; 8 [0-9] ) echo "Digit";; 9 * ) echo "Punctuation, whitespace, or other";; 10 esac 11 exit 0
1.5.3、select 語句
尤其擅長於交互式使用。用戶可以從一組不同的值中進行選擇。
select var in … ; do break done
例子:
1 #!/bin/sh 2 echo "What is your favourite OS?" 3 select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do 4 break 5 done 6 echo "You have selected $var"
#下面是該腳本運行的結果:
What is your favourite OS?
1) Linux
2) Gnu Hurd
3) Free BSD
4) Other
#? 1
You have selected Linux
1.6、循環語句
1.6.1、for語句
for var in 數組列表; do #command bolock done
例子1:
1 #!/bin/bash 2 for var in A B C ; do 3 echo "var is $var" 4 done
例子2:
1 #!/bin/sh 2 #列出 RPM 的數目 3 # 用法: showrpm rpmfile1 rpmfile2 ... 4 # EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm 5 for rpmpackage in $*; do 6 if [ -r "$rpmpackage" ];then 7 echo "== $rpmpackage ==" 8 rpm -qi -p $rpmpackage 9 else 10 echo "ERROR: cannot read file $rpmpackage" 11 fi 12 done
例子3:
1 for var1 in "$@" 2 do 3 #statements 4 done
例2和例3的 $* 和“$@”是相同的
1.6.2、while 語句
while [ express ]; do #command Done
例子1:
1 count=1 2 while [ -n "$*"] 3 do 4 echo "this is a parameter number $count $1" 5 shift 6 count='expr $count + 1' 7 done
例子2:
1 while [ -n "$1" ]; do 2 case $1 in 3 -h) help;shift 1;; # function help is called 4 # 執行 help 函數 , shift 1 表示,移除第一個變量 $1 ,則第二個變為: $1 5 -f) opt_f=1;shift 1;; # variable opt_f is set 6 -l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2 7 --) shift;break;; # end of options 8 -*) echo "error: no such option $1. -h for help";exit 1;; 9 *) break;; 10 esac 11 done
就像平常執行命令一樣,當有參數-h 時,則執行相應的動作
1.6.3、breake 和continue
關鍵字”break” 用來跳出循環。而關鍵字”continue”用來不執行余下的部分而直接跳到下一個循環。
1.6.4、shift
shift將存放在位置變量中的命令行參數,依次向左傳遞.例如
位置變量當前值為:
$1=file1 $2=file2 $3=file3
執行一次shift命令后,位置變量的值為:
$1=file2 $2=file3
還可以在shift命令中指定位置變量轉移的次數, 如:
shift n
例子:
1 while [ "$1"] 2 do 3 if [ "$1"="-i"] then 4 infile=” $2″ 5 shift 2 6 else if [ "$1"="-o"] then 7 outfile=”$2″ 8 shift 2 9 else 10 echo “Program $0 does not recognize option $1″ 11 fi 12 done
二、函數
腳本 b2d 將二進制數 (比如 1101) 轉換為相應的十進制數。這也是一個用expr命令進行數學運算的例子:
1 #!/bin/sh 2 # vim: set sw=4 ts=4 et: 3 help() 4 { 5 cat < b2h -- convert binary to decimal 6 USAGE: b2h [-h] binarynum 7 OPTIONS: -h help text 8 EXAMPLE: b2h 111010 9 will return 58 10 HELP 11 exit 0 12 } 13 14 15 error() 16 { # print an error and exit 17 echo "$1" 18 exit 1 19 } 20 21 lastchar() 22 { # return the last character of a string in $rval 23 if [ -z "$1" ]; then 24 # empty string 25 rval="" 26 return 27 fi 28 # wc puts some space behind the output this is why we need sed: 29 numofchar=`echo -n "$1" wc -c sed ''s/ //g'' ` 30 # now cut out the last char 31 rval=`echo -n "$1" cut -b $numofchar` 32 } 33 34 chop() 35 { # remove the last character in string and return it in $rval 36 if [ -z "$1" ]; then 37 # empty string 38 rval="" 39 return 40 fi 41 # wc puts some space behind the output this is why we need sed: 42 numofchar=`echo -n "$1" wc -c sed ''s/ //g'' ` 43 if [ "$numofchar" = "1" ]; then 44 # only one char in string 45 rval="" 46 return 47 fi 48 numofcharminus1=`expr $numofchar "-" 1` 49 # now cut all but the last char: 50 rval=`echo -n "$1" cut -b 0-${numofcharminus1}` 51 } 52 53 while [ -n "$1" ]; do 54 case $1 in 55 -h) help;shift 1;; # function help is called 56 --) shift;break;; # end of options 57 -*) error "error: no such option $1. -h for help";; 58 *) break;; 59 esac 60 done 61 62 # The main program 63 sum=0 64 weight=1 65 # one arg must be given: 66 [ -z "$1" ] && help 67 binnum="$1" 68 binnumorig="$1" 69 70 while [ -n "$binnum" ]; do 71 lastchar "$binnum" 72 if [ "$rval" = "1" ]; then 73 sum=`expr "$weight" "+" "$sum"` 74 fi 75 # remove the last position in $binnum 76 chop "$binnum" 77 binnum="$rval" 78 weight=`expr "$weight" "*" 2` 79 done 80 echo "binary $binnumorig is decimal $sum"
=============== 三、shell程序調試 ===============
在編程過程中難免會出錯,有的時候,調試程序比編寫程序花費的時間還要多,Shell程序同樣如此。 Shell程序的調試主要是利用bash命令解釋程序的選擇項。
調用bash的形式是:
bash -選擇項Shell程序文件名幾個常用的選擇項是:
-e 如果一個命令失敗就立即退出。
-n 讀入命令但是不執行它們。
-u 置換時把未設置的變量看做出錯。
-v 當讀入Shell輸入行時把它們顯示出來。
-x 執行命令時把命令和它們的參數顯示出來。
=============== 四、信號 ===============
trap命令用於在Shell程序中捕捉信號,之后可以有3種反應方式:
(1)執行一段程序來處理這一信號。
(2)接受信號的默認操作。
(3)忽視這一信號。
trap對上面3種方式提供了3種基本形式:
第一種形式的trap命令在Shell接收到與signal list清單中數值相同的信號時,將執行雙引號中的命令串。
trap ‘commands’ signal-list
trap “commands” signal-list
為了恢復信號的默認操作,使用第二種形式的trap命令:
trap signal-list
第三種形式的trap命令允許忽略信號:
trap ” ” signal-list
注意:
(1)對信號11(段違例)不能捕捉,因為Shell本身需要捕捉該信號去進行內存的轉儲。
(2)在trap中可以定義對信號0的處理(實際上沒有這個信號),Shell程序在其終止(如執行exit語句)時發出該信號。
(3)在捕捉到signal-list中指定的信號並執行完相應的命令之后,如果這些命令沒有將Shell程序終止的話,Shell程序將繼續執行收到信號時所執行的命令后面的命令,這樣將很容易導致Shell程序無法終止。
另外,在trap語句中,單引號和雙引號是不同的。當Shell程序第一次碰到trap語句時,將把commands中的命令掃描一遍。此時若commands是用單引號括起來的話,那么Shell不會對commands中的變量和命令進行替換,否則commands中的變量和命令將用當時具體的值來替換。
=============== 五、bash中常用的命令Alias |設置命令別名 ===============
Bg |將一個被掛起的進程在后台執行
cd |改變用戶的當前目錄
exit |終止一個shell
export |使作為這個命令的參數的變量及其當前值,在當前運行的shell的子進程中可見
fc |編輯當前的命令行歷史列表
fg |讓一個被掛起的進程在前台執行
help |顯示bash內部命令的幫助信息
history |顯示最近輸入的一定數量的命令行
kill |終止一個進程
pwd |顯示用戶當前工作目錄
unalias |刪除命令行別名