作者:王姍姍,華清遠見嵌入式學院講師。
一、 shell中系統變數、使用者變數和環境變數之間的區別和使用:
Shell有以下幾種基本類型的變數:
1) shell定義的環境變數
shell在開始執行時就已經定義了一些和系統的工作環境有關的變數,這些變數使用者還可以重新定義,常用的shell環境變數有:
HOME:用於儲存註冊目錄的完全路徑名。
PATH:用於儲存用冒號分隔的目錄路徑名,shell將按PATH變數中給出的順序搜尋這些目錄,找到的第一個與命令名稱一致的可執行檔將被執行。
TERM:終端的類型。
UID:目前使用者的標識符,取值是由數字構成的字串。
PWD:當前工作目錄的絕對路徑名,該變數的取值隨cd命令的使用而變化。
PS1:主提示符,在特權使用者下,預設的主提示符是“#”,在普通使用者下,預設的主提示符是“$”。
PS2:在shell接收使用者輸入命令的過程中,如果使用者在輸入行的末尾輸入“/”然後斷行符號,或者當使用者按斷行符號鍵時shell判斷出使用者輸入的命令沒有結束時,顯示這個輔助提示符,提示使用者繼續輸入命令的其餘部分,預設的輔助提示符是“>”。
2) 使用者定義的變數
使用者可以按照下面的文法規則定義自己的變數:
變數名=變數值
要注意的一點是,在定義變數時,變數名前不應加符號“$”,在引用變數的內容時則應在變數名前加“$”;在給變數賦值時,等號兩邊一定不能留空格,若變數中本身就包含了空格,則整個字串都要用雙引號括起來。
在編寫shell程式時,為了使變數名和命令名相區別,建議所有的變數名都用大寫字母來表示。
有時我們想要在說明一個變數並對它設定為一個特定值後就不在改變它的值,這可以用下面的命令來保證一個變數的唯讀性:
readly 變數名
在任何時候,建立的變數都只是當前shell的局部變數,所以不能被shell啟動並執行其他命令或shell程式所利用,export命令可以將一局部變數提供給shell執行的其他命令使用,其格式為:
export 變數名
也可以在給變數賦值的同時使用export命令:
export 變數名=變數值
使用export說明的變數,在shell以後啟動並執行所有命令或程式中都可以訪問到。
3) 位置參數
位置參數是一種在調用shell程式的命令列中按照各自的位置決定的變數,是在程式名之後輸入的參數。位置參數之間用空格分隔,shell取第一個位置參數替換程式檔案中的$1,第二個替換$2,依次類推。$0是一個特殊的變數,它的內容是當前這個shell程式的檔案名稱,所以,$0不是一個位置參數,在顯示當前所有的位置參數時是不包括$0的。
4) 預定義變數
預定義變數和環境變數相類似,也是在shell一開始時就定義了的變數,所不同的是,使用者只能根據shell的定義來使用這些變數,而不能重定義它。所有預定義變數都是由$符和另一個符號組成的,常用的shell預定義變數有:
$#:位置參數的數量
$*:所有位置參數的內容
$?:命令執行後返回的狀態
$$:當前進程的進程號
$!:後台啟動並執行最後一個進程號
$0:當前執行的進程名
其中,“$?”用於檢查上一個命令執行是否正確(在Linux中,命令退出狀態為0表示該命令正確執行,任何非0值表示命令出錯)。
“$$”變數最常見的用途是用作臨時檔案的名字以保證臨時檔案不會重複。
5) 參數置換的變數
shell提供了參數置換能力以便使用者可以根據不同的條件來給變數賦不同的值。參數置換的變數有四種,這些變數通常與某一個位置參數相聯絡,根據指定的位置參數是否已經設定類決定變數的取值,它們的文法和功能分別如下。
a. 變數=${參數-word}:如果設定了參數,則用參數的值置換變數的值,否則用word置換。即這種變數的值等於某一個參數的值,如果該參數沒有設定,則變數就等於word的值。
b. 變數=${參數=word}:如果設定了參數,則用參數的值置換變數的值,否則把變數設定成word然後再用word替換參數的值。注意,位置參數不能用於這種方式,因為在shell程式中不能為位置參數賦值。
c. 變數=${參數?word}:如果設定了參數,則用參數的值置換變數的值,否則就顯示word並從shell中退出,如果省略了word,則顯示標準資訊。這種變數要求一定等於某一個參數的值,如果該參數沒有設定,就顯示一個資訊,然後退出,因此這種方式常用於出錯指示。
d. 變數=${參數+word}:如果設定了參數,則用word置換變數,否則不進行置換。
所有這四種形式中的“參數”既可以是位置參數,也可以是另一個變數,只是用位置參數的情況比較多。
二、 shell程式設計的流程式控制制
1) test測試命令
test命令用於檢查某個條件是否成立,它可以進行數值、字元和檔案三個方面的測試,其測試符和相應的功能分別如下:
(1)數值測試:
-eq:等於則為真
-ne:不等於則為真
-gt:大於則為真
-ge:大於等於則為真
-lt:小於則為真
-le:小於等於則為真
(2)字串測試:
=:等於則為真
!=:不相等則為真
-z字串:字串長度偽則為真
-n字串:字串長度不偽則為真
(3)檔案測試:
-e檔案名稱:如果檔案存在則為真
-r檔案名稱:如果檔案存在且可讀則為真
-w檔案名稱:如果檔案存在且可寫則為真
-x檔案名稱:如果檔案存在且可執行則為真
-s檔案名稱:如果檔案存在且至少有一個字元則為真
-d檔案名稱:如果檔案存在且為目錄則為真
-f檔案名稱:如果檔案存在且為普通檔案則為真
-c檔案名稱:如果檔案存在且為字元型特殊檔案則為真
-b檔案名稱:如果檔案存在且為塊特殊檔案則為真
另外,Linux還提供了與(“!”)、或(“-o)、非(“-a”)三個邏輯操作符用於將測試條件串連起來,其優先順序為:“!”最高,“-a”次之,“-o”最低。
同時,bash也能完成簡單的算術運算,格式如下:
$[expression]
例如:var1=2
var2=$[var1*10+1]
則:var2的值為21。
2) if條件陳述式
shell程式中的條件分支是通過if條件陳述式來實現的,其一般格式為:
if條件命令串
then
條件為真時的命令串
else
條件為假時的命令串
fi
3) for迴圈
for迴圈對一個變數的可能的值都執行一個命令序列。賦給變數的幾個數值既可以在程式內以數值列表的形式提供,也可以在程式以外以位置參數的形式提供。for迴圈的一般格式為:
for變數名
[in數值列表]
do
若干個命令列
done
變數名可以是使用者選擇的任何字串,如果變數名是var,則在in之後給出的數值將順序替換迴圈命令列表中的$var。如果省略了in,則變數var的取值將是位置參數。對變數的每一個可能的賦值都將執行do和done之間的命令列表。
4) while和until迴圈
while和until命令都是用命令的返回狀態值來控制迴圈的。While迴圈的一般格式為:
while
若干個命令列1
do
若干個命令列2
done
只要while的“若干個命令列1”中最後一個命令的返回狀態為真,while迴圈就繼續執行do...done之間的“若干個命令列2”。
until命令是另一種迴圈結構,它和while命令相似,其格式如下:
until
若干個命令列1
do
若干個命令列2
done
until迴圈和while迴圈的區別在於:while迴圈在條件為真時繼續執行迴圈,而until則是在條件為假時繼續執行迴圈。
Shell還提供了true和false兩條命令用於建立無限迴圈結構的需要,它們的返回狀態分別是總為0或總為非0
5) case條件選擇
if條件陳述式用於在兩個選項中選定一項,而case條件選擇為使用者提供了根據字串或變數的值從多個選項中選擇一項的方法,其格式如下:
case string in
exp-1)
若干個命令列1
;;
exp-2)
若干個命令列2
;;
……
*)
其他命令列
esac
shell通過計算字串string的值,將其結果依次和運算式exp-1、exp-2等進行比較,直到找到一個匹配的運算式為止,如果找到了匹配項則執行它下面的命令直到遇到一對分號(;;)為止。
在case運算式中也可以使用shell的萬用字元(“*”、“?”、“[ ]”)。通常用“*”作為case命令的最後運算式以便使在前面找不到任何相應的匹配項時執行“其他命令列”的命令。
6) 無條件控制語句break和continue
break用於立即終止當前迴圈的執行,而contiune用於不執行迴圈中後面的語句而立即開始下一個迴圈的執行。這兩個語句只有放在do和done之間才有效。
7) 函數定義
在shell中還可以定義函數。函數實際上也是由若干條shell命令組成的,因此它與shell程式形式上是相似的,不同的是它不是一個單獨的進程,而是shell程式的一部分。函數定義的基本格式為:
functionname
{
若干命令列
}
調用函數的格式為:
functionname param1 param2……
shell函數可以完成某些例行的工作,而且還可以有自己的退出狀態,因此函數也可以作為if、while等控制結構的條件。
在函數定義時不用帶參數說明,但在調用函數時可以帶有參數,此時shell將把這些參數分別賦予相應的位置參數$1、$2、...及$*。
8) 命令分組
在shell中有兩種命令分組的方法:“()”和“{}”,前者當shell執行()中的命令時將再建立一個新的子進程,然後這個子進程去執行圓括弧中的命令。當使用者在執行某個命令時不想讓命令運行時對狀態集合(如位置參數、環境變數、當前工作目錄等)的改變影響到下面語句的執行時,就應該把這些命令放在圓括弧中,這樣就能保證所有的改變只對子進程產生影響,而父進程不受任何幹擾;{}用於將順序執行的命令的輸出結果用於另一個命令的輸入(管道方式)。當我們要真正使用圓括弧和花括弧時(如計算運算式的優先順序),則需要在其前面加上轉義符(/)以便讓shell知道它們不是用於命令執行的控制所用。
9) 訊號
trap命令用於在shell程式中捕捉到訊號,之後可以有三種反應方式:
(1)執行一段程式來處理這一訊號
(2)接受訊號的預設操作
(3)忽視這一訊號
trap對上面三種方式提供了三種基本形式:
第一種形式的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中的變數和命令將用當時具體的值來替換。