網站服務器一個多少錢知名品牌營銷策略
本章主要介紹如何使用bash寫腳本。
- 了解通配符
- 了解變量
- 了解返回值和數值運算
grep的用法是“grep 關鍵字 file”,意思是從file中過濾出含有關鍵字的行。
例如,grep root /var/log/messages,意思是從/var/log/messages 中過濾出含有root
的行。這里很明確的是過濾含有“root”的行。
如果想在/var/log/messages 中過濾出含有IP地址的行呢?IP地址就是一類字符,例如,
1.1.1.1是一個IP,192.168.26.100也是一個IP,那么用什么能表示出來這一類字符呢?
不管是通配符還是正則表達式,都是為了模糊匹配,為了匹配某一類內容,而不是具體的
某個關鍵字。通配符一般用在shell語言中,正則表達式一般用在其他語言中。
不管是通配符還是正則表達式,主要是理解它們的元字符,然后用元字符來組合成我們想
要的那一類字符,本章主要講解通配符的使用。
像我們平時說的張某某,這個某就是一個元字符,不是一個定值。指的是姓張,名字含有
2個字。張某某可能匹配到張二狗,也可能匹配到張阿貓,但是無法匹配到李阿三,也匹配
不了張三,因為張某某匹配的是姓名為3個字的,但是張三這個姓名只有2個字。
如果說有一個人姓“張”名“某”,那么需要匹配“張某”這個人,而不是要匹配張三、
張四,可以用張\某,某前加個“\”表示轉義的意思。
22.1 通配符
通配符一般用在shell語言中,通配符中常見的元字符如下。
(1)[]:匹配一個字符,匹配的是出現(xiàn)在中括號中的字符。
(2)[abc]:匹配一個字符,且只能是a或b或c。
(3)[a-z]:“-”有特殊意義,表示“到”的意思,這里表示a~z,即匹配任一字母。
(4)[0-9]:表示匹配任一數字。
如果想去除含有特殊意義的字符,前面加“\”表示轉義,即去除此字符的特殊意義。 (5)[a\-z]:這里的“-”就沒有“到”的意思了,匹配的是“a”或“-”或“z”這三個
中的一個。
如果想表示“除了”的意思,則在第一個中括號后面加“!”或“^”。
(6)[!a-z]、[^a-z]:表示除字母外的其他字符。
(7)?:表示一個任意字符,這里強調是一個,不是0個也不是多個,但不能匹配表示隱藏
文件的點。
(8)*:表示任意多個任意字符,可以是0個,也可以是1個或多個,但不能匹配表示隱藏
文件的點。
練習:先創(chuàng)建目錄xx并在目錄中創(chuàng)建如下幾個測試文件,命令如下。
[root@pp ~]# mkdir xx
[root@pp ~]# cd xx
[root@pp xx]# touch 1_aa aa11 Aa11 _aaa aa.txt f1aa u_12 flaa
[root@pp xx]#
找出首字符是字母、第二個字符是數字的文件,命令如下。
[root@pp xx]# ls [a-z][0-9]*
f1aa
[root@pp xx]#
找出首字符是字母、第二個字符不是數字的文件,命令如下。
[root@pp xx]# ls [a-z][^0-9]*
aa11 Aa11 aa.txt flaa u_12
[root@pp xx]#
如果要更精確,可以用如下元字符。
(1)[[:upper:]]:純大寫。
(2)[[:lower:]]:小寫。
(3)[[:alpha:]]:字母。 (4)[[:alnum:]:字母和數字。
(5)[[:digit:]]:數字。
列出首字符是小寫字母、第二個字符是數字的文件,命令如下。
[root@pp xx]# ls [[:lower:]][0-9]*
f1aa
[root@pp xx]#
列出首字符是大寫字母、第二個字符是數字或字母的文件,命令如下。
[root@pp xx]# ls [[:upper:]][[:alnum:]]*
Aa11
[root@pp xx]#
22.2 變量
所謂變量,指的是可變的值,并非具體的值。例如,我自己嘴中發(fā)出的“我”,指的是我
自己,張三嘴中發(fā)出的“我”,指的是張三,那么這個“我”就是一個變量。
變量可以分為本地變量、環(huán)境變量、位置變量和預定義變量。
22.2.1 本地變量
定義本地變量的格式如下。
1 變量名=值
定義變量有以下幾點需要注意。
(1)變量名可以包含_、數字、大小寫字母,但不能以數字開頭。
(2)“=”兩邊不要有空格。
(3)“值”如果含有空格,要使用單引號''或雙引號""引起來。
(4)定義變量時,變量名前是不需要加$的,引用變量時需要在變量名前加$。
本章實驗都放在~/yy中練習,命令如下。
[root@pp ~]# mkdir yy ; cd yy
[root@pp yy]#
下面開始練習定義變量,命令如下。
[root@pp yy]# 1aa=123
bash: 1aa=123: 未找到命令..
這里定義變量不正確,因為變量名不能以數字開頭,這里定義變量不正確,因為變量名不能以數字開頭
這里正確地定義了一個變量。
在使用本地變量時,變量名前需要加$,命令如下。
[root@pp yy]# aa=123
[root@pp yy]# echo $aa
123
[root@pp yy]#
本地變量的特點是只能影響當前shell,不能影響子shell。
[root@pp yy]# echo $aa
123
[root@pp yy]# echo $$
3070
[root@pp yy]#
當前shell的PID是3070。下面打開一個子shell。
[root@pp yy]# bash
[root@pp yy]# echo $$
3372
[root@pp yy]#
這個子shell 的PID是3372。
可以看到,沒有aa變量。
[root@pp yy]# echo $aa[root@pp yy]# exit
[root@pp yy]# echo $aa
123
[root@pp yy]#
再次退回到原來的bash,又有了aa變量,情形如圖

定義變量除剛才顯式的定義外,還可以使用如下兩
種方法。
方法1:把一個命令的結果賦值給一個變量,這個
變量要使用$()括起來,或者用反引號“引起來。這里是反引號,與波浪號~是同--個鍵,不是
單引號。
例如,定義一個名稱是ip的變量,對應的值是ens160的IP,命令如下。
[root@pp yy]# ip=$(ifconfig ens160 | awk '/inet /{print $2}')
[root@pp yy]# echo $ip
192.168.248.45
[root@pp yy]#
方法2:通過read命令來獲取變量。
read的用法如下。
1 read ‐p "提示信息" 變量
當遇到read命令時,系統(tǒng)會等待用戶輸入,用戶所輸入的值會賦值給read后面的變量,
命令如下。
[root@pp yy]# read -p "請輸入你的名字" aa
請輸入你的名字iu
[root@pp yy]# echo $aa
iu
[root@pp yy]#
當執(zhí)行read這條命令時,系統(tǒng)會提示用戶輸人一些內容,所輸入的內容會賦值給aa變
量。這里我們輸入的是 tom,所以打印aa變量時,看到的值是tom。
這樣的用法比較適合寫需要和用戶交互的腳本。
22.2.2 環(huán)境變量
定義環(huán)境變量的注意點和本地變量是一樣的。在定義環(huán)境變量時,前面加上export 即可,
命令如下。
[root@pp yy]# export bb=123
[root@pp yy]#
要想查看所有的環(huán)境變量,可以執(zhí)行env命令。
環(huán)境變量的特點是可以影響子shell,這里強調的是子shell,不能影響父shell。
[root@pp yy]# echo $$
3070
[root@pp yy]# echo $bb
123
[root@pp yy]#
當前shell的PID是3828,里面有一個環(huán)境變量 bb。
[root@pp yy]# bash
[root@pp yy]# echo $$
3828
[root@pp yy]# echo $bb
123
[root@pp yy]#
打開一個子shell,里面可以看到bb變量的值,說明環(huán)境變量已經影響到
子shell 了。
在子 shell中重新給bb賦值為456,然后退回到父shell。
[root@pp yy]# export bb=456
[root@pp yy]# exit
exit
[root@pp yy]# echo $bb
123
[root@pp yy]#
可以看到,在父shell 中,bb的值仍然是123,說明在子shell 中定義的變量不會影響到父
shell,如圖22-2所示。

系統(tǒng)中默認已經存在很多個變量,如下所示。
(1)UID:表示當前用戶的uid。
(2)USER:表示當前用戶名。
(3)HOME:表示當前用戶的家目錄。 分別顯示這些變量的值,命令如下。
[root@pp yy]# echo $UID
0
[root@pp yy]# echo $USER
root
[root@pp yy]#
22.2.3 位置變量和預定義變量
運行腳本時,有時后面是需要加上參數的。但是我們在寫腳本時并不能預知后期在腳本后
面跟上什么參數,這時就能用到位置變量了,位置變量如下。
$0:表示腳本的名稱。
$1:表示第1個參數。
$2:表示第2個參數。
...... ${10}:表示第10個參數。
......
這里$后面的數字如果不是個位數,則要用{}括起來。
系統(tǒng)中還內置了一些預定義變量。
$#:表示參數的個數。
$*:表示所有的參數。
例1:寫一個帶參數的腳本,內容如下。
[root@pp yy]# cat scl.yaml
#/bin/bash
echo "這是我第一個腳本,腳本名稱是 $0"
echo "第 1 個參數是:$1"
echo "第 2 個參數是:$2"
echo "第 3 個參數是:$3"
echo "此腳本一共有 $# 個參數,它們分別是:$*"
[root@pp yy]#
給這個腳本加上可執(zhí)行權限,并加參數運行,命令如下。
[root@pp yy]# chmod +x scl.yaml
[root@pp yy]# ./scl.yaml tom bob mary
這是我第一個腳本,腳本名稱是 ./scl.yaml
第 1 個參數是:tom
第 2 個參數是:bob
第 3 個參數是:mary
此腳本一共有 3 個參數,它們分別是:tom bob mary
[root@pp yy]#
運行這個腳本時,共指定了3個參數:tom、bob、mary,它們分別賦值給了
$1、$2、$3。這里S#被自動賦值為3,因為總共有3個參數,所有的參數被賦值給$*。
22.3 返回值
執(zhí)行某命令之后,結果不是正確的就是錯誤的。命令正確執(zhí)行了,返回值為0,如果沒有
正確執(zhí)行則返回值為非零。返回值為非零,不一定是語法錯誤,執(zhí)行結果如果有“否定”的
意思,返回值也為非零。例如, ping 192.168.26.3,語法沒有錯誤,但是沒有ping通,返回值
也為非零。
返回值記錄在$?中,且$?只記錄剛剛執(zhí)行過命令的返回值。因為$?的值會被新執(zhí)行命令
的返回值覆蓋。
練習:先執(zhí)行一個 xxx命令,命令如下。
[root@pp yy]# xxx
bash: xxx: 未找到命令...
[root@pp yy]# echo $?
127
[root@pp yy]# echo $?
0
[root@pp yy]#
先執(zhí)行一個xxx命令,這個命令是錯誤的命令,$?記錄的是剛剛執(zhí)行過xxx命令的返回值。
所以,查看$?的值是127,是一個非零的值。再次查看$?的值時,卻變成了0,因為這個$?
記錄的不再是xxx命令的返回值,而是它前面執(zhí)行過的echo $?命令的返回值。 邏輯上“否定”的意思也是可以體現(xiàn)出來的。例如,下面的例子。
[root@pp yy]# grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@pp yy]# echo $?
0
[root@pp yy]#
這里在/etc/passwd過濾行開頭為root的行,結果找到了,所以返回值為0。
[root@pp yy]# grep ^rootxxx /etc/passwd
[root@pp yy]# echo $?
1
[root@pp yy]#
這里在/etc/passwd過濾行開頭為rootxxx的行,結果沒有找到,即使語法沒有錯誤,但
是邏輯上有“否定”的意思,所以返回值為非零。
22.4數值運算
在寫腳本時,有時我們經常要做一些數學運算。數學運算的符號如下。
(1)+:表示加。
(2)-:表示減。
(3)*:表示乘。
(4)/:表示除。
(5)**:表示次方。
進行數學運算的表達式有$(())、$[]、let等,命令如下。
[root@pp yy]# echo $((2+3))
5
[root@pp yy]#
其中$(O)和$[]的用法是一樣的,如果不用這樣的表達式,看如下代碼。
[root@pp yy]# echo 2**3
2**3
[root@pp yy]#
可以實現(xiàn)定義aa為整數類型,然后再做數學運算,命令如下。
[root@pp yy]# declare -i aa
[root@pp yy]# aa=1+2
[root@pp yy]# echo $aa
3
[root@pp yy]#
首先declare -i aa把aa定義為一個整數,所以1+2等于3,然后賦值給aa.所以aa的值為3。
以上表達式不能求得小數,如果要得到小數需要使用 bc 命令,用法如下。
1 echo "scale=N ; 算法 | bc"
這里N是一個數字,表示小數點后面保留幾位。
計算2/3,小數點后面保留3位,命令如下。
[root@pp yy]# echo "scale=3 ; 2/3" | bc
.666
[root@pp yy]#
這里得到的結果是0.666,整數部分的0沒有顯示。
計算7/6,小數點后面保留3位,命令如下。
[root@pp yy]# echo "scale=3 ; 7/6" | bc
1.166
[root@pp yy]#