Shell腳本是Debian操作系統(tǒng)中常用的一種腳本語言,用于執(zhí)行系統(tǒng)管理和自動化任務(wù)。本教程將介紹Debian Shell腳本的各個方面,包括POSIX shell兼容性、Shell參數(shù)、條件語句、循環(huán)、環(huán)境變量、命令行處理順序以及用于Shell腳本的應(yīng)用程序等內(nèi)容。
一、POSIX shell兼容性
系統(tǒng)中的許多腳本都可以通過任意 POSIX shell來執(zhí)行。
- 默認(rèn)的非交互 POSIX shell “/usr/bin/sh” 是一個指向到 /usr/bin/dash 的符號鏈接,并被許多系統(tǒng)程序使用。
- 默認(rèn)的交互式 POSIX shell 是 /usr/bin/bash。
避免編寫具有 bashisms(bash 化)或者 zshisms(zsh 化)語法的 shell 腳本,確保腳本在所有 POSIX shell 之間具有可移植性。你可以使用 checkbashisms(1) 對其進(jìn)行檢查。
典型 bashism 語法列表:
好的:POSIX | 應(yīng)該避免的:bashism |
---|---|
if [ "$foo" = "$bar" ] ; then … |
if [ "$foo" == "$bar" ] ; then … |
diff -u file.c.orig file.c |
diff -u file.c{.orig,} |
mkdir /foobar /foobaz |
mkdir /foo{bar,baz} |
funcname() { … } |
function funcname() { … } |
八進(jìn)制格式:”\377 “ |
十六進(jìn)制格式:”\xff “ |
使用 “echo” 命令的時候需要注意以下幾個方面,因為根據(jù)內(nèi)置 shell 和外部命令的不同,它的實現(xiàn)也有差別。
注意:
- 避免使用除“-n”以外的任何命令行選項;
- 避免在字符串中使用轉(zhuǎn)義序列,因為根據(jù) shell 不同,計算后的結(jié)果也不一樣;
- 盡管“-n”選項并不是 POSIX 語法,但它已被廣泛接受;
- 如果你想要在輸出字符串中嵌入轉(zhuǎn)義序列,用 “printf” 命令替代 “echo” 命令。
二、Shell參數(shù)
特殊的 shell 參數(shù)經(jīng)常在 shell 腳本里面被用到。
shell 參數(shù)列表:
shell 參數(shù) | 值 |
---|---|
$0 |
shell 或 shell 腳本的名稱 |
$1 |
第一個 shell 參數(shù) |
$9 |
第 9 個 shell 參數(shù) |
$# |
位置參數(shù)數(shù)量 |
"$*" |
"$1 $2 $3 $4 … " |
"$@" |
"$1" "$2" "$3" "$4" … |
$? |
最近一次命令的退出狀態(tài)碼 |
$$ |
這個 shell 腳本的 PID |
$! |
最近開始的后臺任務(wù) PID |
如下所示是需要記憶的基本的參數(shù)展開。
shell 參數(shù)展開列表:
參數(shù)表達(dá)式形式 | 如果?var ?變量已設(shè)置那么值為 |
如果?var ?變量沒有被設(shè)置那么值為 |
---|---|---|
${var:-string} |
“$var “ |
“string “ |
${var:+string} |
“string “ |
“null “ |
${var:=string} |
“$var “ |
“string ” (并運行 “var=string “) |
${var:?string} |
“$var “ |
在?stderr?中顯示 “string ” (出錯退出) |
以上這些操作中 “:” 實際上都是可選的。
- 有 “:” 等于測試的 var 值是存在且非空;
- 沒有 “:” 等于測試的 var 值只是存在的,可以為空。
重要的 shell 參數(shù)替換列表:
參數(shù)替換形式 | 結(jié)果 |
---|---|
${var%suffix} |
刪除位于 var 結(jié)尾的 suffix 最小匹配模式 |
${var%%suffix} |
刪除位于 var 結(jié)尾的 suffix 最大匹配模式 |
${var#prefix} |
刪除位于 var 開頭的 prefix 最小匹配模式 |
${var##prefix} |
刪除位于 var 開頭的 prefix 最大匹配模式 |
三、Shell條件語句
每個命令都會返回退出狀態(tài),這可以被條件語句使用。
- 成功:0 (“True”);
- 失?。悍?0 (“False”)。
注意:
- “0” 在 shell 條件語句中的意思是 “True”,然而 “0” 在 C 條件語句中的含義為 “False”;
- “[” 跟 test 命令是等價的,它評估到 “]” 之間的參數(shù)來作為一個條件表達(dá)式。
如下所示是需要記憶的基礎(chǔ) 條件語法:
- “command && if_success_run_this_command_too || true”
- “command || if_not_success_run_this_command_too || true”
如下所示是多行腳本片段:
if [ conditional_expression ]; then if_success_run_this_command else if_not_success_run_this_command fi
這里末尾的“|| true”是需要的,它可以保證這個 shell 腳本在不小心使用了“-e”選項而被調(diào)用時不會在該行意外地退出。
在條件表達(dá)式中進(jìn)行文件比較:
表達(dá)式 | 返回邏輯真所需的條件 |
---|---|
-e?file |
file?存在 |
-d?file |
file?存在并且是一個目錄 |
-f?file |
file?存在并且是一個普通文件 |
-w?file |
file?存在并且可寫 |
-x?file |
file?存在并且可執(zhí)行 |
file1?-nt?file2 |
file1?是否比?file2?新 |
file1?-ot?file2 |
file1?是否比?file2?舊 |
file1?-ef?file2 |
file1?和?file2?位于相同的設(shè)備上并且有相同的 inode 編號 |
在條件表達(dá)式中進(jìn)行字符串比較:
表達(dá)式 | 返回邏輯真所需的條件 |
---|---|
-z?str |
str?的長度為零 |
-n?str |
str?的長度不為零 |
str1?=?str2 |
str1?和?str2?相等 |
str1?!=?str2 |
str1?和?str2?不相等 |
str1?<?str2 |
str1?排列在?str2?之前(取決于語言環(huán)境) |
str1?>?str2 |
str1?排列在?str2?之后(取決于語言環(huán)境) |
算術(shù)整數(shù)的比較在條件表達(dá)式中為 “-eq”,”-ne”,”-lt”,”-le”,”-gt” 和 “-ge”。
四、shell循環(huán)
這里有幾種可用于 POSIX shell 的循環(huán)形式。
- “for x in foo1 foo2 … ; do command ; done”,該循環(huán)會將 “foo1 foo2 …” 賦予變量 “x” 并執(zhí)行 “command”;
- “while condition ; do command ; done”,當(dāng) “condition” 為真時,會重復(fù)執(zhí)行 “command”;
- “until condition ; do command ; done”,當(dāng) “condition” 為假時,會重復(fù)執(zhí)行 “command”;
- “break” 可以用來退出循環(huán);
- “continue” 可以用來重新開始下一次循環(huán)。
注意:C 語言中的數(shù)值迭代可以用 seq(1) 實現(xiàn)來生成 “foo1 foo2 …”;
五、Shell環(huán)境變量
普通的 shell 命令行提示下的一些常見的環(huán)境變量,可能在你的腳本的執(zhí)行環(huán)境中不存在。
- 對于 “$USER”, 使用 “$(id -un)”;
- 對于 “$UID”, 使用 “$(id -u)”;
- 對于 “$HOME”,使用”$(getent passwd “$(id -u)”|cut -d “:” -f 6)” 。
六、shell命令行處理順序
shell 大致以下列的順序來處理一個腳本。
1、shell 讀取一行。
2、如果該行包含有”…” 或 ‘…’,shell 對該行各部分進(jìn)行分組作為 一個標(biāo)識(one token) (譯注:one token 是指 shell 識別的一個結(jié)構(gòu)單元).
3、shell 通過下列方式將行中的其它部分分隔進(jìn) 標(biāo)識(tokens)。
- 空白字符:空格 tab 換行符;
- 元字符: < > | ; & ( )。
4、shell 會檢查每一個不位于 “…” 或 ‘…’ 的 token 中的 保留字 來調(diào)整它的行為。
- 保留字:if then elif else fi for in while unless do done case esac
5、shell 展開不位于 “…” 或 ‘…’ 中的 別名。
6、shell 展開不位于 “…” 或 ‘…’ 中的 波浪線。
- “~” → 當(dāng)前用戶的家目錄;
- “~user” → user 的家目錄。
7、shell 將不位于 ‘…’ 中的 變量 展開為它的值。
- 變量:”$PARAMETER” 或 “${PARAMETER}”
8、shell 展開不位于 ‘…’ 中的 命令替換。
- “$( command )” → “command” 的輸出;
- “` command `” → “command” 的輸出。
9、shell 將不位于 “…” 或 ‘…’ 中的 glob 路徑 展開為匹配的文件名。
- * → 任何字符;
- ? → 一個字符;
- […] → 任何位于 “…” 中的字符。
10、shell 從下列幾方面查找 命令 并執(zhí)行。
- 函數(shù)定義;
- 內(nèi)建命令;
- “$PATH” 中的可執(zhí)行文件。
shell 前往下一行,并按照這個順序從頭再次進(jìn)行處理。
雙引號中的單引號是沒有效果的。在 shell 中執(zhí)行 “set -x” 或使用 “-x” 選項啟動 shell 可以讓 shell 顯示出所有執(zhí)行的命令。
七、shell腳本應(yīng)用程序
為了使 shell 程序在 Debian 系統(tǒng)上盡可能地具有可移植性,應(yīng)該只使用 必要的軟件包所提供的應(yīng)用程序。
- “aptitude search ~E”,列出 必要的軟件包;
- “dpkg -L package_name |grep ‘/man/man.*/'”,列出 package_name 軟件包所提供的 man 手冊。
包含用于 shell 腳本的小型應(yīng)用程序的軟件包:
軟件包 | 流行度 | 大小 | 說明 |
dash | V:883, I:997 | 191 | 小和快的 POSIX 兼容 shell,用于 sh |
coreutils | V:879, I:999 | 18307 | GNU 核心工具 |
grep | V:781, I:999 | 1266 | GNU grep、egrep 和 fgrep |
sed | V:787, I:999 | 987 | GNU sed |
mawk | V:437, I:997 | 285 | 小和快的 awk |
debianutils | V:907, I:999 | 223 | 用于 Debian 的各種工具 |
bsdutils | V:519, I:999 | 356 | 來自 4.4BSD-Lite 的基礎(chǔ)工具 |
bsdextrautils | V:582, I:698 | 339 | 來自 4.4BSD-Lite 的額外的工具 |
moreutils | V:15, I:38 | 244 | 額外的 Unix 工具 |
雖然 moreutils 這套工具集可能專屬于 Debian 系統(tǒng),它卻包含了一些頗為實用的小工具。在這些工具中,尤其值得一提的是 sponge(8);當(dāng)你需要覆蓋原文件時,這個工具會顯得特別方便。