一文詳解shell程式設計(shell程式設計筆記)
本文為快速入門教程,如果你所在的行業需要用到很深入的shell指令碼知識,可參考書籍《Linux命令列與shell指令碼程式設計大全》,豆瓣連結:https://book.douban.com/subject/26854226/
1. 簡介
常見的程式語言分為兩類:一個是編譯型語言,它們執行前全部一起要經過編譯器的編譯。另一個解釋型語言,執行時,需要使用直譯器一行一行地轉換為程式碼,相關的語言分類如下:
真正能夠控制計算機硬體(CPU、記憶體、顯示器等)的只有作業系統核心(Kernel),圖形介面和命令列只是架設在使用者和核心之間的一座橋樑。
在Linux下,這個命令列程式叫做Shell。由此可見,Shell 是將核心、程式和使用者連線了起來。常見的 Shell 有 sh、bash、csh、tcsh、ash 等。
- sh:sh 的全稱是 Bourne shell,由 AT&T 公司的 Steve Bourne開發,為了紀念他,就用他的名字命名了。sh 是 UNIX 上的標準 shell,很多 UNIX 版本都配有 sh。sh 是第一個流行的 Shell。
- bash:全稱Bourne Again shell,bash shell 是 Linux 的預設 shell。
- csh:sh 之後另一個廣為流傳的 shell 是由柏克萊大學的 Bill Joy 設計的,這個 shell 的語法有點類似C語言,所以才得名為 C shell ,簡稱為 csh。
- tcsh:tcsh 是 csh 的增強版,加入了命令補全功能,提供了更加強大的語法支援。
- ash:一個簡單的輕量級的 Shell,佔用資源少,適合運行於低記憶體環境。
可以通過命令 cat /etc/shells
命令來檢視自己電腦的相容的shell版本,另外,還可以通過命令echo $SHELL
來檢視自己電腦預設的SHELL環境。
$ cat /etc/shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
$ echo $SHELL
/bin/bash
2. 執行shell指令碼的幾種方式
在你的工作空間新建一個文字test.sh(副檔名並不影響指令碼執行,副檔名.sh不寫,或者隨便取也可以,只不過約定俗成用.sh),然後輸入以下內容:
#!/bin/bash
echo "Hello World !"
第一行為註釋,表示使用bash的shell,不寫也無所謂。在終端輸入以下命令都可以執行該指令碼:
./test.sh #方式1
source test.sh # 方式2
sh test.sh # 方式3
bash test.sh # 方式4
/bin/sh test.sh #方式5
/bin/bash test.sh # 方式6
3. 註釋
- 單行註釋
#--------------------------------------------
# 這是一個註釋
# 這是一個註釋
# 這是一個註釋
# 這是一個註釋
#--------------------------------------------
- 多行註釋
:<<EOF
註釋內容...
註釋內容...
註釋內容...
EOF
其中EOF可以用其他符號代替
4. 變數
變數的命名規則與C/C++等類似,它的規則如下:
- 命名只能使用英文字母,數字和下劃線,首個字元不能以數字開頭。
- 不能使用bash裡的關鍵字(可用終端輸入help命令檢視保留關鍵字)。
4.1 系統變數
系統變數也稱為環境變數,所有的程式都可以訪問環境變數,一般用大寫字母表示,例如$HOME、$PWD、$SHELL、$USER等。例如:
$ echo $HOME
/home/myComputer
4.2 區域性變數
區域性變數也稱為自定義變數,區域性變數在指令碼或命令中定義,僅在當前shell例項中有效,其他shell啟動的程式不能訪問區域性變數。
1)定義和使用變數
$ my_name="QLee" #定義變數,=號左右兩側不能有空格
$ echo $my_name #變數的使用
$ echo ${my_name} #變數的使用
QLee
QLee
變數外的括號,是可選的,加花括號是為了幫助直譯器識別變數的邊界。
2)只讀變數
#!/bin/bash
my_name="QLee"
readonly my_name #指定只讀變數,不用$
my_name="lisa"
輸出:
test.sh: 行 4: my_name: 只讀變數
3)刪除變數
#!/bin/bash
my_name="QLee"
unset my_name #刪除變數,不用$
echo $my_name
以上echo不會有輸出,因為my_name被刪除。另外,注意不能刪除只讀變數
。
4)字串
shell只有數字和字串的資料型別,所以這裡介紹一下字串型別。
- 單引號形式
str='this is a string'
單引號裡的任何字元都會原樣輸出,單引號字串中的變數是無效的。單引號只能成對的出現,即使使用轉義符,也不能單獨出現。
- 雙引號形式
my_name='QLee'
str="Hello,my name is \"$my_name\"! " #拼接
echo $str
輸出:
Hello,my name is "QLee"!
- 字串拼接
my_name="QLee"
# 使用雙引號拼接
greeting="hello, "$my_name" !"
greeting_1="hello, ${my_name} !"
echo $greeting $greeting_1
# 使用單引號拼接
greeting_2='hello, '$my_name' !'
greeting_3='hello, ${my_name} !' # 原樣輸出!!!
echo $greeting_2 $greeting_3
輸出:
hello, QLee ! hello, QLee !
hello, QLee ! hello, ${my_name} !
- 獲取字串長度
string="abcd"
echo ${#string} #輸出 4
- 提取字串
從位置1開始,提取4個字元
string="my name is QLee"
echo ${string:1:4} # 從第1個字元開始擷取4個字元,輸出y na
- 查詢字串
以下表示查詢n或i的第一個位置
string="my name is QLee"
echo `expr index "$string" ni` # 輸出4
5. shell陣列
bash中只有一維陣列,它的下標從0開始,陣列的大小沒有限定,定義陣列時用空格分開,例如:
array_name=(value0 value1 value2 value3)# 陣列的定義
或者:
array_name=(
value0
value1
value2
value3
)
或者:
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
5.1 讀取陣列
my_array=(A B "C" D)
echo "第一個元素為: ${my_array[0]}"
echo "第二個元素為: ${my_array[1]}"
echo "第三個元素為: ${my_array[2]}"
echo "第四個元素為: ${my_array[3]}"
輸出:
第一個元素為: A
第二個元素為: B
第三個元素為: C
第四個元素為: D
5.2 獲取陣列長度
# 取得陣列元素的個數
length=${#array_name[@]} # @或*可以獲取陣列中的所有元素。
# 或者
length=${#array_name[*]}
# 取得陣列單個元素的長度
lengthn=${#array_name[n]}
6. 基本運算子
原生bash不支援簡單的數學運算,但是可以通過其他命令來實現,例如 awk 和 expr,expr 最常用。expr 是一款表示式計算工具,使用它能完成表示式的求值操作。
關於運算子需要注意以下兩點:
- 表示式和運算子之間要有空格,例如 2+2 是不對的,必須寫成 2 + 2;
- 完整的表示式要被
包含,注意這個字元不是常用的單引號,在 Esc 鍵下邊。
6.1 算數運算
下表列出了常用的算術運算子,假定變數 a 為 10,變數 b 為 20:
運算子 | 說明 | 舉例 |
---|---|---|
+ | 加法 | `expr $a + $b` 結果為 30。 |
- | 減法 | `expr $a - $b` 結果為 -10。 |
* | 乘法 | `expr $a \* $b` 結果為 200。 |
/ | 除法 | `expr $b / $a` 結果為 2。 |
% | 取餘 | `expr $b % $a` 結果為 0。 |
= | 賦值 | a=$b 將把變數 b 的值賦給 a。 |
== | 相等。用於比較兩個數字,相同則返回 true。 | [ $a == $b ] 返回 false。 |
!= | 不相等。用於比較兩個數字,不相同則返回 true。 | [ $a != $b ] 返回 true。 |
注意:
- *的前面需要用"\",其他符號不用
- 條件表示式要放在方括號之間,並且要
有空格
,例如: [$a==$b] 是錯誤的,必須寫成 [ $a == $b ]。
6.2 關係運算符
關係運算符只支援數字,不支援字串,除非字串的值是數字。下表列出了常用的關係運算符,假定變數 a 為 10,變數 b 為 20:
運算子 | 單詞 | 說明 | 舉例 |
---|---|---|---|
-eq | equal | 相等 | [ $a -eq $b ] 返回 false。 |
-ne | not equal | 不相等 | [ $a -ne $b ] 返回 true。 |
-gt | great than | 大於 | [ $a -gt $b ] 返回 false。 |
-lt | less than | 小於 | [ $a -lt $b ] 返回 true。 |
-ge | great than or equal | 大於或等於 | [ $a -ge $b ] 返回 false。 |
-le | less than or equal | 小於等於 | [ $a -le $b ] 返回 true。 |
6.3 布林運算子
下表列出了常用的布林運算子,假定變數 a 為 10,變數 b 為 20:
運算子 | 說明 | 舉例 |
---|---|---|
! | 非運算 | [ ! false ] 返回 true。 |
-o | 或運算 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 與運算 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
6.4 邏輯運算子
以下介紹 Shell 的邏輯運算子,假定變數 a 為 10,變數 b 為 20:
運算子 | 說明 | 舉例 |
---|---|---|
&& | 邏輯"與" | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 邏輯"或" | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
注意:必須兩個"[ ]"
6.5 字串運算子
下表列出了常用的字串運算子,假定變數 a 為 “abc”,變數 b 為 “efg”:
運算子 | 說明 | 舉例 |
---|---|---|
= | 是否相等 | [ $a = $b ] 返回 false。 |
!= | 是否不相等 | [ $a != $b ] 返回 true。 |
-z | 是否字串長度為0 | [ -z $a ] 返回 false。 |
-n | 是否字元創長度不為0 | [ -n “$a” ] 返回 true。 |
$ | 字串是否為空,不為空返回 true。 | [ $a ] 返回 true。 |
6.6 檔案測試運算子
檔案測試運算子用於檢測 Unix 檔案的各種屬性。屬性檢測描述如下:
操作符 | 說明 | 舉例 |
---|---|---|
-b file | 檢測檔案是否是塊裝置檔案,如果是,則返回 true。 | [ -b $file ] |
-c file | 檢測檔案是否是字元裝置檔案,如果是,則返回 true。 | [ -c $file ] |
-d file | 檢測檔案是否是目錄,如果是,則返回 true。 | [ -d $file ] |
-f file | 檢測檔案是否是普通檔案(既不是目錄,也不是裝置檔案),如果是,則返回 true。 | [ -f $file ] |
-g file | 檢測檔案是否設定了 SGID 位,如果是,則返回 true。 | [ -g $file ] |
-k file | 檢測檔案是否設定了粘著位(Sticky Bit),如果是,則返回 true。 | [ -k $file ] |
-p file | 檢測檔案是否是有名管道,如果是,則返回 true。 | [ -p $file ] |
-u file | 檢測檔案是否設定了 SUID 位,如果是,則返回 true。 | [ -u $file ] |
-r file | 檢測檔案是否可讀,如果是,則返回 true。 | [ -r $file ] |
-w file | 檢測檔案是否可寫,如果是,則返回 true。 | [ -w $file ] |
-x file | 檢測檔案是否可執行,如果是,則返回 true。 | [ -x $file ] |
-s file | 檢測檔案是否為空(檔案大小是否大於0),不為空返回 true。 | [ -s $file ] |
-e file | 檢測檔案(包括目錄)是否存在,如果是,則返回 true。 | [ -e $file ] |
7. echo命令
echo為顯示命令,它有下列幾種用法。
- 1)顯示普通字串
echo "It is a test" # It is a test
- 2)顯示轉移字元
echo "\"It is a test\"" # "It is a test"
- 3)顯示變數
read name # 從標準輸入讀取一行給name
echo "$name It is a test"
- 4)顯示換行
echo -e "OK! \n" # -e 開啟轉義
echo "It is a test"
輸出:
OK!
It is a test
- 5)不顯示換行
echo -e "OK! \c" # -e 開啟轉義 \c 不換行
echo "It is a test"
輸出:
OK! It is a test
- 6)顯示結果定向至檔案
echo "It is a test" > myfile
- 原樣輸出字串,不進行轉義或取變數(用單引號)
echo '$name\"' #輸出:$name\"
- 7)顯示命令執行結果
echo `date`# `為反引號,顯示現在的時間
8. printf命令
它的使用形式:
printf format-string [arguments...]
說明:
- format-string: 為格式控制字串
- arguments: 為引數列表。
預設 printf 不會像 echo 自動新增換行符,我們可以手動新增 \n。
$ echo "Hello, Shell"
Hello, Shell
$ printf "Hello, Shell\n"
Hello, Shell
格式輸出中,%s 輸出一個字串,%d 整型輸出,%c 輸出一個字元,%f 輸出實數,以小數形式輸出。舉例說明如下:
printf "%-10s %-8s %-4s\n" name 性別 體重kg
printf "%-10s %-8s %-4.2f\n" QLee 男 65.3
printf "%-10s %-8s %-4.2f\n" lisa 男 75.6
printf "%-10s %-8s %-4.2f\n" yoyo 女 45.39
輸出:
printf 的轉義序列
序列 | 說明 |
---|---|
\a | 警告字元,通常為ASCII的BEL字元 |
\b | 後退 |
\c | 不顯示輸出結果中任何結尾的換行字元(只在%b格式指示符控制下的引數字串中有效),而且,任何留在引數裡的字元、任何接下來的引數以及任何留在格式字串中的字元,都被忽略 |
\f | 換頁(formfeed) |
\n | 換行 |
\r | 回車(Carriage return) |
\t | 水平製表符 |
\v | 垂直製表符 |
\\ | 一個字面上的反斜槓字元 |
\ddd | 表示1到3位數八進位制值的字元。僅在格式字串中有效 |
\0ddd | 表示1到3位的八進位制值字元 |
9. 流程控制
9.1 if語句
- 情況1:if
if condition
then
command1
command2
...
commandN
fi
- 情況2:if else
if condition
then
command1
command2
...
commandN
else
command
fi
- 情況3:if else-if else
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
9.2 for迴圈
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
舉例:
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done
輸出:
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
for str in This is a string
do
echo $str
done
輸出:
This
is
a
string
9.3 while語句
語法格式:只要condition為true,則繼續執行
while condition
do
command
done
無限迴圈:
while :
do
command
done
或者:
while true
do
command
done
或者
for (( ; ; ))
9.4 until迴圈
語法格式:直至條件為 true 時停止
until condition
do
command
done
9.5 case … esac多分支語句
每個 case 分支用右圓括號開始,用兩個分號 ;; 表示執行結束。
基本語法:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
9.6 break和continue
和c語言的break和continue語句一樣使用。
- break:跳出整個迴圈
- continue:跳出這一次的迴圈,繼續執行一次迴圈。
10. 函式
shell中函式的定義格式如下:
[ function ] funname [()]
{
action;
[return int;]
}
說明:
- 可以帶function fun() 定義,也可以直接fun() 定義,不帶任何引數。
- 引數返回,可以顯示加:return 返回,如果不加,將以最後一條命令執行結果,作為返回值。
10.1 無引數、無返回值的函式
demoFun(){
echo "這是我的第一個 shell 函式!"
}
echo "-----函式開始執行-----"
demoFun
echo "-----函式執行完畢-----"
輸出:
-----函式開始執行-----
這是我的第一個 shell 函式!
-----函式執行完畢-----
10.2 無引數、有返回值的函式
funWithReturn(){
echo "這個函式會對輸入的兩個數字進行相加運算..."
echo "輸入第一個數字: "
read aNum
echo "輸入第二個數字: "
read anotherNum
echo "兩個數字分別為 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "輸入的兩個數字之和為 $? !"
輸出:
這個函式會對輸入的兩個數字進行相加運算...
輸入第一個數字:
1
輸入第二個數字:
2
兩個數字分別為 1 和 2 !
輸入的兩個數字之和為 3 !
說明:
- 函式返回值在呼叫該函式後通過 $? 來獲得。
- 所有函式在使用前必須定義
10.3 有引數的函式
funWithParam(){
echo "第一個引數為 $1 !"
echo "第二個引數為 $2 !"
echo "第十個引數為 $10 !"
echo "第十個引數為 ${10} !"
echo "第十一個引數為 ${11} !"
echo "引數總數有 $# 個!"
echo "作為一個字串輸出所有引數 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
輸出:
第一個引數為 1 !
第二個引數為 2 !
第十個引數為 10 !
第十個引數為 34 !
第十一個引數為 73 !
引數總數有 11 個!
作為一個字串輸出所有引數 1 2 3 4 5 6 7 8 9 34 73 !
注意:當n>=10時,需要使用${n}來獲取引數。
還有幾個特殊字元用來處理引數:
引數處理 | 說明 |
---|---|
$# | 傳遞到指令碼或函式的引數個數 |
$* | 以一個單字串顯示所有向指令碼傳遞的引數 |
$$ | 指令碼執行的當前程序ID號 |
$! | 後臺執行的最後一個程序的ID號 |
[email protected] | 與$*相同,但是使用時加引號,並在引號中返回每個引數。 |
$- | 顯示Shell使用的當前選項,與set命令功能相同。 |
$? | 顯示最後命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤。 |
11. 輸入/輸出重定向
預設情況下,標準輸入和輸出的地方都是終端,但是我們可以通過命令對輸入/輸出進行重定向。
重定向命令列表如下:
命令 | 說明 |
---|---|
command > file | 將輸出重定向到 file。 |
command < file | 將輸入重定向到 file。 |
command >> file | 將輸出以追加的方式重定向到 file。 |
n > file | 將檔案描述符為 n 的檔案重定向到 file。 |
n >> file | 將檔案描述符為 n 的檔案以追加的方式重定向到 file。 |
n >& m | 將輸出檔案 m 和 n 合併。 |
n <& m | 將輸入檔案 m 和 n 合併。 |
<< tag | 將開始標記 tag 和結束標記 tag 之間的內容作為輸入。 |
需要注意的是檔案描述符 0 通常是標準輸入(STDIN),1 是標準輸出(STDOUT),2 是標準錯誤輸出(STDERR)。
- Here Document
Here Document 是 Shell 中的一種特殊的重定向方式,用來將輸入重定向到一個互動式 Shell 指令碼或程式。
例如:
cat << EOF
hello, everybody
my name is QLee
EOF
列印如下:
hello, everybody
my name is QLee
- /dev/null 檔案
/dev/null 是一個特殊的檔案,寫入到它的內容都會被丟棄;如果嘗試從該檔案讀取內容,那麼什麼也讀不到。但是 /dev/null 檔案非常有用,將命令的輸出重定向到它,會起到"禁止輸出"的效果。
如果希望執行某個命令,但又不希望在螢幕上顯示輸出結果,那麼可以將輸出重定向到 /dev/null:
$ command > /dev/null
如果希望遮蔽 stdout 和 stderr,可以這樣寫:
$ command > /dev/null 2>&1 # 0 是標準輸入(STDIN),1 是標準輸出(STDOUT),2 是標準錯誤輸出(STDERR)。
12. 檔案包含
與C語言中的#include語句類似,shell中也有自己的包含命令,它的語法格式如下:
. filename # 注意點號(.)和檔名中間有一空格
或
source filename
因為是解釋語言,所以檔案包含的意義,其實就是寫一條命令執行被包含的檔案。
13. 其他命令
(1)data
顯示當前時間,格式如下:
date [選項] [+時間格式] #使用(+)號開始的引數用來指定時間格式
例如:
nowTime=`date +%Y-%M-%d-%H:%M:%S`
echo $nowTime
date的格式有以下幾種:
%H 小時(以00-23來表示)。
%I 小時(以01-12來表示)。
%K 小時(以0-23來表示)。
%l 小時(以0-12來表示)。
%M 分鐘(以00-59來表示)。
%P AM或PM。
%r 時間(含時分秒,小時以12小時AM/PM來表示)。
%s 總秒數。起算時間為1970-01-01 00:00:00 UTC。
%S 秒(以本地的慣用法來表示)。
%T 時間(含時分秒,小時以24小時制來表示)。
%X 時間(以本地的慣用法來表示)。
%Z 市區。
%a 星期的縮寫。
%A 星期的完整名稱。
%b 月份英文名的縮寫。
%B 月份的完整英文名稱。
%c 日期與時間。只輸入date指令也會顯示同樣的結果。
%d 日期(以01-31來表示)。
%D 日期(含年月日)。
%j 該年中的第幾天。
%m 月份(以01-12來表示)。
%U 該年中的週數。
%w 該周的天數,0代表週日,1代表週一,異詞類推。
%x 日期(以本地的慣用法來表示)。
%y 年份(以00-99來表示)。
%Y 年份(以四位數來表示)。
%n 在顯示時,插入新的一行。
%t 在顯示時,插入tab。
MM 月份(必要)
DD 日期(必要)
hh 小時(必要)
mm 分鐘(必要)
ss 秒(選擇性)
(2)後臺執行指令碼
test.sh &
對於基礎應用,以上知識已經足夠了。由於本人所在行業原因,暫時還不需要那麼高深的shell知識,如果後續有深入研究,會繼續補充更新。
給大家推薦一個很讚的Linux下的命令列網址:https://www.linuxcool.com/