Linux系統程式設計(15)——shell指令碼語法

尹成發表於2014-07-25

Shell字串

字串是shell程式設計中最常用最有用的資料型別(除了數字和字串,也沒啥其它型別好用了),字串可以用單引號,也可以用雙引號,也可以不用引號。單雙引號的區別跟PHP類似。

單引號

 

str='this is a string'

單引號字串的限制:

單引號裡的任何字元都會原樣輸出,單引號字串中的變數是無效的;

單引號字串中不能出現單引號(對單引號使用轉義符後也不行)。

雙引號

your_name='qinjx'
str="Hello, I know your are\"$your_name\"! \n"


雙引號的優點:

雙引號裡可以有變數

雙引號裡可以出現轉義字元


拼接字串 

your_name="qinjx"
greeting="hello,"$your_name" !"
greeting_1="hello, ${your_name}!"
echo $greeting $greeting_1


獲取字串長度

string="abcd"
echo ${#string} #輸出 4


提取子字串

string="alibaba is a greatcompany"
echo ${string:1:4} #輸出liba


查詢子字串

string="alibaba is a greatcompany"
echo `expr index "$string" is`


Shell陣列

Linux Shell在程式設計方面比Windows批處理強大很多,無論是在迴圈、運算。

 

bash支援一維陣列(不支援多維陣列),並且沒有限定陣列的大小。類似與C語言,陣列元素的下標由0開始編號。獲取陣列中的元素要利用下標,下標可以是整數或算術表示式,其值應大於或等於0。

定義陣列

 

在Shell中,用括號來表示陣列,陣列元素用“空格”符號分割開。定義陣列的一般形式為:

    陣列名=(值1 值2 ... 值n)

例如:

array_name=(value0 value1 value2 value3)


或者

array_name=(
value0
value1
value2
value3
)


還可以單獨定義陣列的各個分量:

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen


可以不使用連續的下標,而且下標的範圍沒有限制。

讀取陣列

 

讀取陣列元素值的一般格式是:

   ${陣列名[下標]}


例如:

valuen=${array_name[n]}


使用@符號可以獲取陣列中的所有元素,例如:

echo ${array_name[@]}


獲取陣列的長度

 

獲取陣列長度的方法與獲取字串長度的方法相同,例如:

# 取得陣列元素的個數
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得陣列單個元素的長度
lengthn=${#array_name[n]}

 

Shelltest命令

 

Shell中的 test 命令用於檢查某個條件是否成立,它可以進行數值、字元和檔案三個方面的測試。

數值測試

引數

說明

-eq

等於則為真

-ne

不等於則為真

-gt

大於則為真

-ge

大於等於則為真

-lt

小於則為真

-le

小於等於則為真

 

例如:

num1=100
num2=100
if test $[num1] -eq $[num2]
then
   echo 'The two numbers are equal!'
else
   echo 'The two numbers are not equal!'
fi


輸出:

The two numbers are equal!

 

字串測試

 

引數

說明

=

等於則為真

!=

不相等則為真

-z 字串

字串長度偽則為真

-n 字串

字串長度不偽則為真

 

例如:

num1=100
num2=100
if test num1=num2
then
   echo 'The two strings are equal!'
else
   echo 'The two strings are not equal!'
fi


輸出:

The two strings are equal!

 

檔案測試

 

引數

說明

-e 檔名

如果檔案存在則為真

-r 檔名

如果檔案存在且可讀則為真

-w 檔名

如果檔案存在且可寫則為真

-x 檔名

如果檔案存在且可執行則為真

-s 檔名

如果檔案存在且至少有一個字元則為真

-d 檔名

如果檔案存在且為目錄則為真

-f 檔名

如果檔案存在且為普通檔案則為真

-c 檔名

如果檔案存在且為字元型特殊檔案則為真

-b 檔名

如果檔案存在且為塊特殊檔案則為真

 

 

例如:

cd /bin
if test -e ./bash
then
   echo 'The file already exists!'
else
   echo 'The file does not exists!'
fi


輸出:

The file already exists!

 

另外,Shell還提供了與( ! )、或( -o )、非( -a )三個邏輯操作符用於將測試條件連線起來,其優先順序為:“!”最高,“-a”次之,“-o”最低。例如:

cd /bin
if test -e ./notFile -o ./bash
then
   echo 'One file exists at least!'
else
   echo 'Both dose not exists!'
fi


輸出:

One file exists at least!

 

條件判斷語句if/then/elif/else/fi

和C語言類似,在Shell中用if、then、elif、else、fi這幾條命令實現分支控制。這種流程控制語句本質上也是由若干條Shell命令組成的,例如先前講過的

 

if [ -f ~/.bashrc ]; then

    .~/.bashrc

fi其實是三條命令,if [ -f~/.bashrc ]是第一條,then . ~/.bashrc是第二條,fi是第三條。如果兩條命令寫在同一行則需要用;號隔開,一行只寫一條命令就不需要寫;號了,另外,then後面有換行,但這條命令沒寫完,Shell會自動續行,把下一行接在then後面當作一條命令處理。和[命令一樣,要注意命令和各引數之間必須用空格隔開。if命令的引數組成一條子命令,如果該子命令的Exit Status為0(表示真),則執行then後面的子命令,如果Exit Status非0(表示假),則執行elif、else或者fi後面的子命令。if後面的子命令通常是測試命令,但也可以是其它命令。Shell指令碼沒有{}括號,所以用fi表示if語句塊的結束。見下例:

 

#! /bin/sh
 
if [ -f /bin/bash ]
then echo "/bin/bash is a file"
else echo "/bin/bash is NOT afile"
fi
if :; then echo "always true";


 

fi:是一個特殊的命令,稱為空命令,該命令不做任何事,但Exit Status總是真。此外,也可以執行/bin/true或/bin/false得到真或假的Exit Status。再看一個例子:

#! /bin/sh
 
echo "Is it morning? Please answer yesor no."
read YES_OR_NO
if [ "$YES_OR_NO" ="yes" ]; then
 echo "Good morning!"
elif [ "$YES_OR_NO" ="no" ]; then
 echo "Good afternoon!"
else
 echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
 exit 1
fi
exit 0

上例中的read命令的作用是等待使用者輸入一行字串,將該字串存到一個Shell變數中。

 

此外,Shell還提供了&&和||語法,和C語言類似,具有Short-circuit特性,很多Shell指令碼喜歡寫成這樣:

 

test "$(whoami)" != 'root'&& (echo you are using a non-privileged account; exit 1)

&&相當於“if...then...”,而||相當於“ifnot...then...”。&&和||用於連線兩個命令,而上面講的-a和-o僅用於在測試表示式中連線兩個測試條件,要注意它們的區別,例如,

 

test "$VAR" -gt 1 -a"$VAR" -lt 3


和以下寫法是等價的

 

test "$VAR" -gt 1 && test"$VAR" -lt

 

case/esac語句

case命令可類比C語言的switch/case語句,esac表示case語句塊的結束。C語言的case只能匹配整型或字元型常量表示式,而Shell指令碼的case可以匹配字串和Wildcard,每個匹配分支可以有若干條命令,末尾必須以;;結束,執行時找到第一個匹配的分支並執行相應的命令,然後直接跳到esac之後,不需要像C語言一樣用break跳出。

 

#! /bin/sh
 
echo "Is it morning? Please answer yesor no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)
 echo "Good Morning!";;
[nN]*)
 echo "Good Afternoon!";;
*)
 echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
 exit 1;;
esac
exit 0


 

使用case語句的例子可以在系統服務的指令碼目錄/etc/init.d中找到。這個目錄下的指令碼大多具有這種形式(以/etc/apache2為例):

 

case $1 in
         start)
                   ...
         ;;
         stop)
                   ...
         ;;
         reload| force-reload)
                   ...
         ;;
         restart)
         ...
         *)
                   log_success_msg"Usage: /etc/init.d/apache2{start|stop|restart|reload|force-reload|start-htcacheclean|stop-htcacheclean}"
                   exit1
         ;;
esac啟動apache2服務的命令是
 
$ sudo /etc/init.d/apache2

 

start$1是一個特殊變數,在執行指令碼時自動取值為第一個命令列引數,也就是start,所以進入start)分支執行相關的命令。同理,命令列引數指定為stop、reload或restart可以進入其它分支執行停止服務、重新載入配置檔案或重新啟動服務的相關命令。

 

for/do/done語句

Shell指令碼的for迴圈結構和C語言很不一樣,它類似於某些程式語言的foreach迴圈。例如:

 

#! /bin/sh
 
for FRUIT in apple banana pear; do
 echo "I like $FRUIT"
done

FRUIT是一個迴圈變數,第一次迴圈$FRUIT的取值是apple,第二次取值是banana,第三次取值是pear。再比如,要將當前目錄下的chap0、chap1、chap2等檔名改為chap0~、chap1~、chap2~等(按慣例,末尾有~字元的檔名錶示臨時檔案),這個命令可以這樣寫:

 

$ for FILENAME in chap?; do mv $FILENAME$FILENAME~; done



也可以這樣寫:

 

$ for FILENAME in `ls chap?`; do mv $FILENAME$FILENAME~; done5.5. while/do/done


while的用法和C語言類似。比如一個驗證密碼的指令碼:

 

#! /bin/sh
 
echo "Enter password:"
read TRY
while [ "$TRY" !="secret" ]; do
 echo "Sorry, try again"
 read TRY
done

下面的例子通過算術運算控制迴圈的次數:

 
#! /bin/sh
 
COUNTER=1
while [ "$COUNTER" -lt 10 ]; do
 echo "Here we go again"
 COUNTER=$(($COUNTER+1))
Done


 

Shell還有until迴圈,類似C語言的do...while迴圈。

 

 

Shell函式

 

shell允許將一組命令集或語句形成一個可用塊,這些塊稱為shell函式。

 

shell中函式的定義格式如下:

函式名(){
   command1
   command2
   ...
   commandN
    [return value ]
}


 如果願意,可在函式名前加上關鍵字function,這取決於使用者。

function 函式名(){
   command1
   command2
   ...
   commandN
    [return value ]
}


 

函式返回值,可以顯示增加return語句;如果不加,則將最後一條命令執行結果作為返回值(一般為0,如果執行失敗則返回錯誤程式碼)。 return後跟數值(0-255)。

 

函式可以放在同一個檔案中作為一段程式碼,也可以放在只包含函式的單獨檔案中。函式不必包含很多語句或命令,甚至可以只包含一個echo語句,這取決於使用者。

 

下面的例子定義了一個函式並進行呼叫:

#!/bin/bash
demoFun(){
   echo "This is your first shell function!"
}
echo "Function begin..."
hello
echo "Function end!"


輸出:

Function begin...

This is your first shell function!

Function end!

 

下面定義一個帶有return語句的函式:

#!/bin/bash
funWithReturn(){
   echo "The function is to get the sum of two numbers..."
   echo -n "Input first number: "
   read aNum
   echo -n "Input another number: "
   read anotherNum
   echo "The two numbers are $aNum and $anotherNum !"
   return $(($aNum+$anotherNum))
}
funWithReturn
echo "The sum of two numbers is $?!"


輸出類似下面:

The function is to get the sum of twonumbers...

Input first number: 25

Input another number: 50

The two numbers are 25 and 50 !

The sum of two numbers is 75 !

 

函式返回值在呼叫該函式後通過 $? 來獲得。

 

注意:所有函式在使用前必須定義。這意味著必須將函式放在指令碼開始部分,直至shell直譯器首次發現它時,才可以使用。呼叫函式僅使用其函式名即可。

相關文章