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
推薦閱讀:
《Linux指令行與shell腳本程式設計大全》
《谷歌shell編碼規範》