Ict-創新/LPI/105.2
考生應該能夠自定義現有指令碼,或編寫簡單的新的 BASH 指令碼。
關鍵知識領域
- 使用標準 sh 語法(迴圈、測試)。
- 使用命令替換。
- 測試返回值以判斷命令成功或失敗或其他資訊。
- 執行有條件的郵件傳送給超級使用者。
- 透過 shebang(#!)行正確選擇指令碼直譯器。
- 管理指令碼的位置、所有權、執行和 suid 許可權。
shell 指令碼是一個文字檔案,它告訴 shell 該做什麼。
它包含用作指令碼其餘內容直譯器的程式的名稱。以 #!ProgramPath+Name 開頭的行(通常是第一行)指定要使用的直譯器
#!/bin/bash 或 #!/bin/sh 或 #!/usr/bin/perl -w
實際上,當系統被要求啟動一個指令碼時,它會讀取以 #! 開頭的行,啟動相應的指令碼直譯器,該直譯器依次讀取指令碼並執行其中包含的命令。
執行指令碼的條件
指令碼檔案必須可由執行它的使用者執行(chmod ....)。直譯器必須位於指令碼指定的位置:預設情況下會呼叫 bash。
shell 指令碼中使用的語言
指令碼語言取決於使用的指令碼直譯器。bash 有自己的語法,可以在互動模式或指令碼中使用。
向指令碼傳遞引數
指令碼可以被賦予最多 9 個位置引數(適用於所有直譯器)或最多 99 個引數(適用於 bash)。
在指令碼內部,每個引數將被標識為 $1 到 $9 或 ${10} 到 ${99}
scriptname param1 param2 param3 param4..... param47.....$0 $1 $2 $3 $4 ${47} .....
一些特殊引數由 Bourne shell 自動設定,通常不能直接設定或修改。
引數 $n 可以透過指令碼內部的 set 命令修改。(其中 n 是 1-99 適用於 bash)
set aaa bbb ccc ... $1 $2 $3
$n - 位置引數 n,最大 n=9($0 是 shell 指令碼的名稱)
${nn} - 位置引數 nn(對於 nn>9)
$# - 位置引數的數量(不包括指令碼程式)
$@, $* - 所有位置引數
"$@" - 與 "$1" "$2" . . . "$n" 相同
"$*" - 與 "$1c$2c . . . $n" 相同,c = $IFS 的內容(預設是空格)
$? - 上一個命令的退出狀態
$$ - 當前 shell 的程序 ID
$- - 當前生效的選項
$! - 上一個後臺命令的程序 ID
$- 當前 shell 的名稱(在本例中為 'bash')
shift 命令
shift 命令將位置引數的賦值向左移動。如果一個
指令碼被這樣呼叫
script1 aaa bbb ccc ddd
並且以下命令在指令碼內部執行
|
# echo $1 $2 $3 # shift # echo $1 $2 $3 |
第一個 echo 命令的結果是
aaa bbb ccc
第二個 echo 命令的結果是
bbb ccc
set 和 unset 命令
unset 命令通常用於取消設定變數的值,set 命令用於從指令碼內部為位置引數賦值。如果一個指令碼在沒有位置引數的情況下啟動,並且在驗證這一點後,指令碼為它們分配預設值,則非常有用。
set aa bb cc dd $1 $2 $3 $4 - 將 aa 賦值給 $1,bb 賦值給 $2,cc 賦值給 $3,dd 賦值給 $4
set 命令也用於改變 bash 行為的屬性。
set 的一個重要選項是
|
# set -o noclobber |
該命令會導致重定向符號 (>) 無法覆蓋現有檔案的內容。
以下列出了最常用的條件指令
if 條件分支指令
if - 允許只有在滿足特定條件時才執行某些命令。
語法:(另請參閱本主題後面的“條件表示式”部分)
if <condition_is_true> ; then
run_these_commands
.................
elseif <condition_is_true> ; then
如果第一個條件不滿足而此條件滿足,則
run_these_commands
.................
else
如果以上所有條件都不滿足,則
run_these_commands_instead
.................
fi
if 指令塊的結束
<condition_is_true> 可以是以下型別
(1) 測試檔案或目錄的狀態。
if test -e /etc/fstab ; then
或
if [ -e /etc/fstab ] ; then
(2) 命令或指令碼退出程式碼。
if (ifconfig | grep 'ppp0') ; then
(3) 變數的內容與某個值相對應
if $1 ; then - 如果 $1 包含值,則為真
if [ "$net" = "eth0" ] ; then - 測試字串
if test ["$#" -eq 5 ] ; then
(整數測試)
case 條件分支指令
case 通常用於根據
變數的內容,有條件地分支到多個選項之一。
語法
case <Variable> in
<choice1>)
要執行的命令
;;
choice2)
要執行的命令
;;
choice3)
要執行的命令
;;
*)
如果以上所有條件都不滿足,則要執行的命令。
;;
case
case 指令塊的結束
指令碼中的迴圈
在需要多次執行一系列命令時使用。
while 條件迴圈指令
while 指令只要其條件(在 while 語句中定義)滿足,就會一直迴圈並執行其塊中的命令。
語法:
while <condition_is_true> ; do
run_these_commands
done
while 指令塊的結束
注意:while 通常用於詢問使用者輸入某種鍵盤輸入,如果響應不合適,則會重複請求,直到輸入正確的資訊。然後退出 while 迴圈,程式繼續執行。
until 條件迴圈指令
until 迴圈的工作方式與 while 迴圈完全相同,只是邏輯相反。
迴圈一直持續到條件滿足。
語法:
until <condition_is_true> ; do
run_these_commands
done
until 指令塊的結束
for 迴圈指令
for 指令允許在一系列命令中執行的次數與給定列表中的專案數量相同。每次迴圈執行時,特定變數的內容都會變成給定列表中當前專案的 value。
語法:
for variable in list ; do
run_these_commands
done
for 指令塊的結束
variable = 將內容變成給定列表中每次迴圈的當前專案的變數名。列表也可以是一個包含專案列表的變數。
for item in ~/file1 ~/file2 ~/file3 ; do
echo "------------ Content of $item -----------"
cat $item >> ~/allfiles
done
Shell 函式是一系列儲存在某個地方的命令,可以在指令碼中的多個位置使用。可以透過位置引數向函式傳遞引數。
位置引數($1、$2、$3 ...)將成為函式的區域性變數。它們使用與指令碼相同的語法,但第一個($0)保持全域性變數。
變數 FUNCNAME 用於與 $0 相似,用於相同的目的。
像 $#、$*、$@ 這樣的特殊變數在函式中也是區域性的。
所有其他變數對於指令碼來說是全域性的,並且可以被函式修改。
命令 return x(x=返回值)可以用作函式退出命令,並用於分配函式返回值。
語法:
FunctionName () {
command ;
command ;
}
或
function FunctionName () {
command ;
command ;
}
(有關 Shell 函式的更多詳細資訊,請參見上一節“自定義和使用 Shell 環境”中的函式。)
退出程式碼和變數 $?
所有程式(包括指令碼)在程序結束時都會返回一個退出程式碼。退出程式碼有助於確定程式或指令碼的成功或失敗。可以透過特殊變數 $? 讀取此退出程式碼,並用於在呼叫指令碼中做出進一步的決策。
通常,“0”的退出程式碼表示成功,任何其他程式碼(1-255)表示某種失敗。它也常被稱為錯誤程式碼。
&& 和 || 條件分支
退出程式碼可用於根據其成功或失敗執行另一個命令(僅一個)。雙安培符號“&&”用於指定在退出程式碼成功(0)時執行的命令。雙管道“||”用於指定在退出程式碼不成功(1-255)時執行的命令。
示例
|
# ifconfig ppp0 && echo "pppd running" || echo "pppd not running" |
如果命令 ifconfig ppp0 成功,則將執行命令 echo "pppd running"(&&),否則將執行命令 echo "pppd not running"。
從指令碼向 root 傳送郵件
有時,向 root 或其他使用者傳送郵件,以宣佈自動指令碼執行中的某些異常或成功是很有用的。通常使用的程式是“mail”。有關它使用的所有選項,請參閱 man mail。
語法1:
| # mail -s "subject" destination_mail_address "message.." |
語法2:
| # program | mail -s "subject" destination_mail_address |
語法3:
|
# mail -s "subject" destination_mail_address < message body....... EOM |
示例:
| # df | mail -s "HD Space on $(date)" root |
將命令 df 的結果傳送到本地 root 使用者。
Bash 指令碼的位置和安全性
管理指令碼通常儲存在 PATH 中,該路徑是 /usr/local/bin 或 /root/bin。正常的訪問許可權為 755(rwx r-x r-x) 或為了透過防止除 root 之外的任何其他使用者執行它來獲得更多保護:700(rwx --- ---)。
雖然 SUID 對指令碼沒有任何影響,但非常舊版本的 Linux 可能會受到 SUID 設定的影響。
條件表示式
[edit | edit source]test 和 [...] 命令用於使用檔案屬性、字串和整數評估條件表示式。基本格式為:test expression 或 [ expression ],其中 expression 是您正在評估的條件。在左括號之後和右括號之前必須有空格。空格還必須分隔表示式引數和運算子。如果表示式計算結果為真,則返回零退出狀態,否則表示式計算結果為假並返回非零退出狀態。
測試檔案運算子
-a <file> 如果檔案存在,則為真。
-b <file> 如果檔案存在且是塊特殊檔案,則為真。
-c <file> 如果檔案存在且是字元特殊檔案,則為真。
-d <file> 如果檔案存在且是目錄,則為真。
-e <file> 如果檔案存在,則為真。
-f <file> 如果檔案存在且是普通檔案,則為真。
-g <file> 如果檔案存在且設定了組 ID,則為真。
-h <file> 如果檔案存在且是符號連結,則為真。
-k <file> 如果檔案存在且其“粘性”位已設定,則為真。
-p <file> 如果檔案存在且是命名管道 (FIFO),則為真。
-r <file> 如果檔案存在且可讀,則為真。
-s <file> 如果檔案存在且大小大於零,則為真。
-t <fd> 如果檔案描述符 fd 已開啟並引用終端,則為真。
-u <file>如果檔案存在且其 SUID 位已設定,則為真。
-w <file>如果檔案存在且可寫,則為真。
-x <file> 如果檔案存在且可執行,則為真。
-O <file> 如果檔案存在且由有效 UID 擁有,則為真。
-G <file> 如果檔案存在且由有效 GID 擁有,則為真。
-L <file> 如果檔案存在且是符號連結,則為真。
-S <file> 如果檔案存在且是套接字,則為真。
-N <file> 如果檔案存在且自上次讀取後已修改,則為真。
file1 -nt file2 如果 file1 比 file2 更新(根據修改日期),或者 file1 存在而 file2 不存在,則為真。
file1 -ot file2 如果 file1 比 file2 舊,或者 file2 存在而 file1 不存在,則為真。
file1 -ef file2 如果 file1 和 file2 引用同一個裝置和 inode 號,則為真。
測試字串運算子
-n string 如果字串的長度不為零,則為真
-z string 如果字串的長度為零,則為真
string 如果字串未設定為 null,則為真
string1 = string2 如果 string1 等於 string2,則為真
string1 = string2 如果 string1 等於 string2,則為真
string1 != string2 如果 string1 不等於 string2,則為真
string1 < string2 如果 string1 在當前語言環境中按字典順序排在 string2 之前,則為真。
string1 > string2 如果 string1 在當前語言環境中按字典順序排在 string2 之後,則為真。
string = pattern 如果字串與模式匹配,則為真
string != pattern 如果字串與模式不匹配,則為真
測試整數運算子
exp1 -eq exp2 如果 exp1 等於 exp2,則為真,例如 [ "$#" -eq 4 ]
exp1 -ne exp2 如果 exp1 不等於 exp2,則為真,例如 test "$#" -ne 3
exp1 -le exp2 如果 exp1 小於或等於 exp2,則為真
exp1 -lt exp2 如果 exp1 小於 exp2,則為真
exp1 -ge exp2 如果 exp1 大於或等於 exp2,則為真
exp1 -gt exp2 如果 exp1 大於 exp2,則為真
其他測試運算子
! exp 如果給定表示式為假,則為真,例如 [ ! -r /etc/motd ]
exp1 -a exp2 如果 exp1 和 exp2 都計算結果為真,則為真(見下面的示例)
exp1 -o exp2如果 exp1 或 exp2 至少有一個計算結果為真,則為真
\( exp \) 如果 exp 為真,則為真;用於對錶達式進行分組
\ 用於轉義括號。在此字元前後使用空格
[ "$A" = "$B" -a \( "$C" = "$D" -a "$E" = "$F" \) ]
以下是使用過的檔案、術語和實用程式的部分列表。* 用於
- while
- test
- if
- read
- seq