Shell 程式設計入門
Shell 是一個用 C 語言編寫的程式,它是使用者使用 Linux 的橋樑。它是作業系統最外層的介面,
負責直接面向使用者互動並提供核心服務。
一、變數
1、 定義
Shell
定義變數時,變數名不加美元符號,如:
content="hello world!"
變數名的命名須遵循如下規則:
- 命名只能使用英文字母,數字和下劃線,首個字元不能以數字開頭。
- 中間不能有空格,可以使用下劃線 _。
- 不能使用標點符號。
- 不能使用bash裡的關鍵字(可用help命令檢視保留關鍵字)。
2、 使用
使用一個定義過的變數,只要在變數名前面加美元符號即可,如:
content="hello world!"
echo $content
echo ${content}
變數名外面的花括號是可選的,加不加都行,加花括號是為了幫助直譯器識別變數的邊界。
content="hello world!"
echo "name:${content}!!!"
推薦給所有變數加上花括號,這是個好的程式設計習慣。
已定義的變數,可以被重新定義,如:
content="hello world!"
echo $content
content="hello shell!"
echo $content
3、 只讀變數
使用 readonly
命令可以將變數定義為只讀變數,只讀變數的值不能被改變。
content="hello world!"
readonly content
content="hello shell!"
執行指令碼,結果如下:
/bin/sh: NAME: This variable is read only.
4、 區域性變數
Shell
中預設定義的變數是全域性變數,可以使用 global
進行顯式宣告,其作用域從被定義的地方開始,一直到指令碼結束或者被刪除的地方。
local
可以定義區域性變數,在函式內部使用。
#!/bin/bash
name="global variable"
function func(){
local name="local variable"
echo $name
}
func
echo $name
# local variable
# global variable
5、 變數型別
在 shell
中會同時存在三種變數:
- 區域性變數;
- 環境變數;
- shell 變數。
二、字串
字串是最常用最有用的資料型別,字串可以用單引號,也可以用雙引號,也可以不用引號。
1、單引號
str='this is a string'
echo '$str'
# $str
單引號字串的限制:
- 單引號裡的任何字元都會原樣輸出,單引號字串中的變數是無效的;
- 單引號字串中不能出現單獨一個的單引號(對單引號使用轉義符後也不行),但可成對出現,作為字串拼接使用。
2、 雙引號
name="shell"
str="Hello, I know you are \"$name\"! \n"
echo $str
# Hello, I know you are "shell"!
雙引號的優點:
- 雙引號裡可以有變數;
- 雙引號裡可以出現轉義字元。
3、 字串長度
string="abcd"
echo ${#string}
# 4
4、 提取子字串
以下例項從字串第 2 個字元開始擷取 4 個字元:
string="huawei is a great compan"
echo ${string:1:4}
# uawe
5、 查詢子字串
查詢字元 i 或 o 的位置(哪個字母先出現就計算哪個):
string="huawei is a great compan"
echo `expr index "$string" io`
# 6
注意: 以上指令碼中 ` 是反引號,而不是單引號 ',不要看錯了哦。
三、陣列
Shell
只支援一維陣列(不支援多維陣列),並且沒有限定陣列的大小。類似於 C
語言,陣列元素的下標由 0 開始編號。
1、 定義陣列
在 shell
中,用括號來表示陣列,陣列元素用"空格"符號分割開。
array=("value0" "value1" "value2" "value3")
還可以單獨定義陣列的各個元素:
array[0]="value0"
array[1]="value1"
array[n]="valuen"
2、 讀取陣列
讀取陣列元素值的一般格式是:
value=${array_name[n]}
使用 @
符號可以獲取陣列中的所有元素,例如:
echo ${array_name[@]}
# value0 value1 value2 value3
3、 獲取長度
獲取陣列長度的方法與獲取字串長度的方法相同,例如:
# 取得陣列元素的個數
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得陣列單個元素的長度
lengthn=${#array_name[n]}
注意:陣列不可以進行切割,錯誤用法 ${array[1:2]}
。
四、流程控制
1、 if 判斷
語法示例判斷兩個變數是否相等。
#!/bin/bash
a=10
b=20
if [ $a == $b ]; then
echo "a 等於 b"
elif [ $a -gt $b ]; then
echo "a 大於 b"
elif [ $a -lt $b ]; then
echo "a 小於 b"
else
echo "沒有符合的條件"
fi
# a 小於 b
不能使用 if [ $a > $b ]
,正確的方式是 if (( $a > $b ))
。
2、 for 迴圈
for
迴圈即執行一次所有命令,空格進行元素分割,使用變數名獲取列表中的當前取值。
示例,順序輸出當前列表中的數字:
#!/bin/bash
for loop in 1 2 3; do
echo "The value is: $loop"
done
#The value is: 1
#The value is: 2
#The value is: 3
迴圈字串內容:
#!/bin/bash
for str in This is a string; do
echo $str
done
# This
# is
# a
# string
迴圈陣列中元素:
#!/bin/bash
array=("value0" "value1" "value2" "value3")
# array[*]與array[@]兩者皆可
for loop in ${array[*]}; do
echo ${loop}
done
# value0
# value1
# value2
# value3
3、 while 迴圈
while
迴圈用於不斷執行一系列命令,也用於從輸入檔案中讀取資料。
以下是一個基本的 while
迴圈,測試條件是:如果 int 小於等於 5,那麼條件返回真。int 從 1 開始,每次迴圈處理時,int 加 1。執行上述指令碼,返回數字 1 到 5,然後終止。
int=1
while [ $int -le 5 ]; do
echo $int
let "int++"
done
無限迴圈
# 方式一
while :
do
command
done
# 方式二
while true
do
command
done
4、 break 終止
在迴圈語句中,可以使用 break
命令,允許跳出所有迴圈(終止執行後面的所有迴圈)。
#!/bin/bash
while :; do
echo -n "輸入 1 到 5 之間的數字:"
read aNum
case $aNum in
1 | 2 | 3 | 4 | 5)
echo "你輸入的數字為 $aNum!"
;;
*)
echo "你輸入的數字不是 1 到 5 之間的! 遊戲結束"
break
;;
esac
done
5、 continue 繼續
continue命令與break命令類似,只有一點差別,它不會跳出所有迴圈,僅僅跳出當前迴圈。
#!/bin/bash
while :; do
echo -n "輸入 1 到 5 之間的數字: "
read aNum
case $aNum in
1 | 2 | 3 | 4 | 5)
echo "你輸入的數字為 $aNum!"
;;
*)
echo "你輸入的數字不是 1 到 5 之間的!"
continue
echo "遊戲結束"
;;
esac
done
執行程式碼發現,當輸入大於5的數字時,該例中的迴圈不會結束,語句 echo "遊戲結束" 永遠不會被執行。
五、函式
1、 函式定義
Shell
中可以使用者定義函式,然後在 shell
指令碼中可以隨便呼叫。
下面的例子定義了一個函式並進行呼叫:
#!/bin/bash
function demo(){
echo "這是我的第一個 shell 函式!"
}
echo "-----函式開始執行-----"
demo
echo "-----函式執行完畢-----"
可以帶 function fun()
定義,也可以直接 fun()
定義,不帶任何引數。
引數返回,可以顯示加:return
返回,如果不加,將以最後一條命令執行結果,作為返回值。 return
後跟數值n(0-255)。
函式指令碼執行結果:
-----函式開始執行-----
這是我的第一個 shell 函式!
-----函式執行完畢-----
2、 函式引數
在 shell
中,呼叫函式時可以向其傳遞引數。在函式體內部,通過 $n 的形式來獲取引數的值,例如,$1 表示第一個引數,$2 表示第二個引數...
帶引數的函式示例:
#!/bin/bash
function funWithParam(){
echo "第一個引數為 $1 !"
echo "第十個引數為 $10 !"
echo "第十個引數為 ${10} !"
echo "第十一個引數為 ${11} !"
echo "引數總數有 $# 個!"
echo "作為一個字串輸出所有引數 $* !"
}
funWithParam 11 22 3 4 5 6 7 8 9 34 73
輸出結果:
第一個引數為 11 !
第十個引數為 110 !
第十個引數為 34 !
第十一個引數為 73 !
引數總數有 11 個!
作為一個字串輸出所有引數 11 22 3 4 5 6 7 8 9 34 73 !
引數獲取時 $n
與 ${n}
還是有區別的,特別是第二行的列印。
$10
不能獲取第十個引數,獲取第十個引數需要 ${10}
。當n>=10時,需要使用 ${n}
來獲取引數。
另外,還有幾個特殊字元用來處理引數:
$# 傳遞到指令碼或函式的引數個數
$* 以一個單字串顯示所有向指令碼傳遞的引數
$$ 指令碼執行的當前程式ID號
$! 後臺執行的最後一個程式的ID號
$@ 與$*相同,但是使用時加引號,並在引號中返回每個引數。
$- 顯示Shell使用的當前選項,與set命令功能相同。
$? 顯示最後命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤。
6、運算子
1、算術運算子
下表列出了常用的算術運算子。
+ 加法
- 減法
* 乘法
/ 除法
% 取餘
= 賦值
== 相等
!= 不相等
注意:條件表示式要放在方括號之間,並且要有空格,例如: [$a==$b]
是錯誤的,必須寫成 [ $a == $b ]
。
使用示例如下:
#!/bin/bash
a=10
b=20
val=$(expr $a + $b)
echo "a + b : $val"
val=$(expr $a - $b)
echo "a - b : $val"
val=$(expr $a \* $b)
echo "a * b : $val"
val=$(expr $b / $a)
echo "b / a : $val"
val=$(expr $b % $a)
echo "b % a : $val"
if [ $a == $b ]; then
echo "a 等於 b"
fi
if [ $a != $b ]; then
echo "a 不等於 b"
fi
還可以使用下面的運算子替換,結果都一致:
#!/bin/bash
a=10
b=20
val=$(expr $a + $b)
echo "a + b : $val"
var=$(($a + $b))
echo "a + b : $val"
var=$[$a + $b]
echo "a + b : $val"
注意:
- 乘號(*)前邊必須加反斜槓()才能實現乘法運算;
- $((表示式)) 此處表示式中的 "*" 不需要轉義符號 ""。
2、關係運算子
關係運算子只支援數字,不支援字串,除非字串的值是數字。
下表列出了常用的關係運算子。
-eq 檢測兩個數是否相等,相等返回 true。
-ne 檢測兩個數是否不相等,不相等返回 true。
-gt 檢測左邊的數是否大於右邊的,如果是,則返回 true。
-lt 檢測左邊的數是否小於右邊的,如果是,則返回 true。
-ge 檢測左邊的數是否大於等於右邊的,如果是,則返回 true。
-le 檢測左邊的數是否小於等於右邊的,如果是,則返回 true。
使用示例如下:
#!/bin/bash
a=10
b=20
if [ $a -eq $b ]; then
echo "$a -eq $b : a 等於 b"
else
echo "$a -eq $b: a 不等於 b"
fi
if [ $a -gt $b ]; then
echo "$a -gt $b: a 大於 b"
else
echo "$a -gt $b: a 不大於 b"
fi
運算子可以使用"=="、"! ="、">"替換:
#!/bin/bash
a=10
b=20
if [ $a == $b ]; then
echo "$a -eq $b : a 等於 b"
else
echo "$a -eq $b: a 不等於 b"
fi
if (($a > $b)); then
echo "$a -gt $b: a 大於 b"
else
echo "$a -gt $b: a 不大於 b"
fi
注意:">"、"> =" 、"<" 、"< =" 不能使用"[]"。
3、邏輯運算子
常用的邏輯運算子。
&& 邏輯的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 邏輯的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true
使用示例如下:
#!/bin/bash
a=10
b=20
if [[ $a -lt 100 && $b -gt 100 ]]; then
echo "返回 true"
else
echo "返回 false"
fi
if [[ $a -lt 100 || $b -gt 100 ]]; then
echo "返回 true"
else
echo "返回 false"
fi
執行指令碼,輸出結果如下所示:
返回 false
返回 true
4、字串運算子
下表列出了常用的字串運算子。
= 檢測兩個字串是否相等,相等返回 true。
!= 檢測兩個字串是否不相等,不相等返回 true。
-z 檢測字串長度是否為0,為0返回 true。
-n 檢測字串長度是否不為 0,不為 0 返回 true。
$ 檢測字串是否為空,不為空返回 true。
字串運算子例項如下:
#!/bin/bash
if [ -z $a ]
then
echo "-z $a : 字串長度為 0"
else
echo "-z $a : 字串長度不為 0"
fi
if [ -n "$a" ]
then
echo "-n $a : 字串長度不為 0"
else
echo "-n $a : 字串長度為 0"
fi
if [ $a ]
then
echo "$a : 字串不為空"
else
echo "$a : 字串為空"
fi
5、檔案測試運算子
檔案測試運算子用於檢測 Unix 檔案的各種屬性。
-b file 檢測檔案是否是塊裝置檔案。
-c file 檢測檔案是否是字元裝置檔案。
-d file 檢測檔案是否是目錄。
-f file 檢測檔案是否是普通檔案。
-g file 檢測檔案是否設定了 SGID 位。
-k file 檢測檔案是否設定了粘著位(Sticky Bit)。
-p file 檢測檔案是否是有名管道。
-u file 檢測檔案是否設定了 SUID 位。
-r file 檢測檔案是否可讀。
-w file 檢測檔案是否可寫。
-x file 檢測檔案是否可執行。
-s file 檢測檔案是否為空。
-e file 檢測檔案。
七、輸入/輸出重定向
1、 輸出重定向
將命令的完整的輸出重定向在使用者檔案中。
# 覆蓋
$ echo "hello world" >./test.file
# 追加
$ echo "hello world" >>./test.file
2、 輸入重定向
從使用者檔案中的內容輸出到命令列。
$ wc -l < ./test.file
1
可以與 while 語句結合,遍歷檔案內容,按行列印:
while read line; do
echo $line
done < ./test.file
3、 標準輸入輸出
一般情況下,每個 Unix/Linux
命令執行時都會開啟三個檔案:
- 標準輸入檔案(stdin):stdin的檔案描述符為0,Unix程式預設從stdin讀取資料。
- 標準輸出檔案(stdout):stdout 的檔案描述符為1,Unix程式預設向stdout輸出資料。
- 標準錯誤檔案(stderr):stderr的檔案描述符為2,Unix程式會向stderr流中寫入錯誤資訊。
預設情況下,command > file 將 stdout 重定向到 file,command < file 將stdin 重定向到 file。
如果希望 stderr 重定向到 file,可以這樣寫:
$ command 2>file
如果希望 stderr 追加到 file 檔案末尾,可以這樣寫:
$ command 2>>file
2 表示標準錯誤檔案(stderr)。
如果希望將 stdout 和 stderr 合併後重定向到 file,可以這樣寫:
$ command > file 2>&1
或者
$ command >> file 2>&1
如果希望對 stdin 和 stdout 都重定向,可以這樣寫:
$ command < file1 >file2
command 命令將 stdin 重定向到 file1,將 stdout 重定向到 file2。
八、eval 函式
當我們在命令列前加上 eval
時,shell
就會在執行命令之前掃描它兩次。eval
命令將首先會先掃描命令列進行所有的置換,然後再執行該命令。該命令適用於那些一次掃描無法實現其功能的變數。該命令對變數進行兩次掃描。
常見的使用場景如下:
1、普通情況
$ var=100
$ echo $var
100
$ eval echo $var
這樣和普通的沒有加 eval
關鍵字的命令的作用一樣。
2、字串轉換命令
$ cat file
helle shell
it is a test of eval
$ tfile="cat file"
$ eval $tfile
helle shell
it is a test of eval
從上面可以看出 eval 經歷了兩次掃描,第一次掃描替換了變數為字串,第二次掃描執行了字串內容。
3、獲取引數
$ cat t.sh
#!/bin/bash
eval echo \$$#
$ ./t.sh a b c
c
$ ./t.sh 1 2 3
3
通過轉義符 “|” 與 $# 結合,可以動態的獲取最後一個引數。
4、 修改指標
$ var=100
$ ptr=var
$ eval echo \$$ptr
100
$ eval $ptr=50
$ echo $val
50
推薦閱讀: