標籤:
一、學習 shell指令碼之前的基礎知識
【linux shell中的特殊符號】
1. * :代表零個或多個字元或數字。
test後面可以沒有任何字元,也可以有多個字元,總之有或沒有都能匹配出來。
2. ? :只代表一個任意的字元
不管是數字還是字母,只要是一個都能匹配出來。
3. # :這個符號在linux中表示注釋說明的意思,即”#”後面的內容linux忽略掉。
在命令的開頭或者中間插入”#” ,linux都會忽略掉的。這個符號在shell指令碼中用的很多。
4. wc :統計文檔的行數、字元數、詞數,常用的選項為:
-l :統計行數
-m :統計字元數
-w :統計詞數
5. >, >>, 2>, 2>> :前面講過重新導向符號> 以及>> 分別表示取代和追加的意思,然後還有兩個符號就是這裡的2> 和2>> 分別表示錯誤重新導向和錯誤追加重新導向,當我們運行一個命令報錯時,報錯資訊會輸出到當前的螢幕,如果想重新導向到一個文本裡,則要用2>或者2>>。
6. [ ] :中括弧,中間為字元組合,代表中間字元中的任意一個
7. && 與 ||
在上面剛剛提到了分號,用於多條命令間的分隔字元。另外還有兩個可以用於多條命令中間的特殊符號,那就是“&&”和”||”。下面筆者把這幾種情況全列出:
1) command1 ; command2
2) command1 && command2
3) command1 || command2
使用”;”時,不管command1是否執行成功都會執行command2; 使用”&&”時,只有command1執行成功後,command2才會執行,否則command2不執行;使用”||”時,command1執行成功後command2 不執行,否則去執行command2,總之command1和command2總有一條命令會執行。
8.test測試命令
test命令用於檢查某個條件是否成立,它可以進行數值、字串和檔案三個方面的測試,其測試符和相應的功能分別如下:
(1)數值測試:
-eq:等於則為真
-ne:不等於則為真
-gt:大於則為真
-ge:大於等於則為真
-lt:小於則為真
-le:小於等於則為真
(2)字串測試:
=:等於則為真
!=:不相等則為真
-z字串:字串長度偽則為真
-n字串:字串長度不偽則為真
(3)檔案測試:
-e檔案名稱:如果檔案存在則為真
-r檔案名稱:如果檔案存在且可讀則為真
-w檔案名稱:如果檔案存在且可寫則為真
-x檔案名稱:如果檔案存在且可執行則為真
-s檔案名稱:如果檔案存在且至少有一個字元則為真
-d檔案名稱:如果檔案存在且為目錄則為真
-f檔案名稱:如果檔案存在且為普通檔案則為真
-c檔案名稱:如果檔案存在且為字元型特殊檔案則為真
-b檔案名稱:如果檔案存在且為塊特殊檔案則為真
另外,Linux還提供了與(“!”)、或(“-o)、非(“-a”)三個邏輯操作符用於將測試條件串連起來,其優先順序為:“!”最高,“-a”次之,“-o”最低。
同時,bash(sh不能運行)也能完成簡單的算術運算,格式如下:
$[expression]
例如:var1=2
var2=$[var1*10+1]
則:var2的值為21。
sh可以運行以下:
$((expression))
例如:var1=2
var2=$[var1*10+1]
則:var2的值為21。
[ ] 實際上是bash 中 test 命令的簡寫。即所有的 [ expr ] 等於 test expr,注意 有空格。
9. (())
使用方法:
文法:
((運算式1,運算式2…))
特點:
1、在雙括弧結構中,所有運算式可以像c語言一樣,如:a++,b--等。
2、在雙括弧結構中,所有變數可以不加入:“$”符號首碼。
3、雙括弧可以進行邏輯運算,四則運算
4、雙括弧結構 擴充了for,while,if條件測試運算
5、支援多個運算式運算,各個運算式之間用“,”分開
使用執行個體:
1234567891011 |
#!/bin/sh a=1; b=2; c=3; ((a=a+1)); echo $a; a=$((a+1,b++,c++)); echo $a,$b,$c |
運行結果:
sh testsh.sh
2
3,3,4
雙括弧結構之間支援多個運算式,然後加減乘除等c語言常用運算子都支援。如果雙括弧帶:$,將獲得運算式值,賦值給左邊變數。
12345678910 |
#!/bin/sh a=1; b= "ab" ; echo $((a>1?8:9)); ((b!= "a" ))&& echo "err2" ; ((a<2))&& echo "ok" ; |
運行結果:
sh testsh.sh
9
err2
ok
12345678910111213141516171819202122 |
#!/bin/sh num=100; total=0; for ((i=0;i<=num;i++)); do ((total+=i)); done echo $total; total=0; i=0; while ((i<=num)); do ((total+=i,i++)); done echo $total; if ((total>=5050)); then echo "ok" ; fi |
運算結果:
sh testsh.sh
5050
5050
ok
有了雙括弧運算子:[[]],[],test 邏輯運算,已經let,expr 都可以拋到一邊了。
二、SHELL 指令碼
有一個問題需要約定一下,凡是自訂的指令碼建議放到/usr/local/sbin/目錄下,這樣做的目的是,一來可以更好的管理文檔;二來以後接管你的管理員都知道自訂指令碼放在哪裡,方便維護。
【shell指令碼的基本結構以及如何執行】
Shell指令碼通常都是以.sh 為尾碼名的,這個並不是說不帶.sh這個指令碼就不能執行,只是大家的一個習慣而已。所以,以後你發現了.sh為尾碼的檔案那麼它一定會是一個shell指令碼了。test.sh中第一行一定是 “#! /bin/bash” 它代表的意思是,該檔案使用的是bash文法。如果不設定該行,那麼你的shell指令碼就不能被執行。’#’表示注釋,在前面講過的。後面跟一些該指令碼的相關注釋內容以及作者和建立日期或者版本等等。當然這些注釋並非必須的,如果你懶的很,可以省略掉,但是筆者不建議省略。因為隨著你工作時間的增加,你寫的shell指令碼也會越來越多,如果有一天你回頭查看你寫的某個指令碼時,很有可能忘記該指令碼是用來幹什麼的以及什麼時候寫的。所以寫上注釋是有必要的。另外系統管理員並非你一個,如果是其他管理員查看你的指令碼,他看不懂豈不是很鬱悶。該指令碼再往下面則為要啟動並執行命令了。
Shell指令碼的執行很簡單,直接”sh filename “ 即可,另外你還可以這樣執行
預設我們用vim編輯的文檔是不帶有執行許可權的,所以需要加一個執行許可權,那樣就可以直接使用’./filename’ 執行這個指令碼了。另外使用sh命令去執行一個shell指令碼的時候是可以加-x選項來查看這個指令碼執行過程的,這樣有利於我們調試這個指令碼哪裡出了問題。
該shell指令碼中用到了’date’這個命令,它的作用就是用來列印當前系統的時間。其實在shell指令碼中date使用率非常高。有幾個選項筆者常常在shell指令碼中用到:
%Y表示年,%m表示月,%d表示日期,%H表示小時,%M表示分鐘,%S表示秒
注意%y和%Y的區別。
-d 選項也是經常要用到的,它可以列印n天前或者n天后的日期,當然也可以列印n個月/年前或者後的日期。
另外星期幾也是常用的
【shell指令碼中的變數】
在shell指令碼中使用變數顯得我們的指令碼更加專業更像是一門語言,開個玩笑,變數的作用當然不是為了專業。如果你寫了一個長達1000行的shell指令碼,並且指令碼中出現了某一個命令或者路徑幾百次。突然你覺得路徑不對想換一下,那豈不是要更改幾百次?你固然可以使用批量替換的命令,但是也是很麻煩,並且指令碼顯得臃腫了很多。變數的作用就是用來解決這個問題的。
在test2.sh中使用到了反引號,你是否還記得它的作用?’d’和’d1’在指令碼中作為變數出現,定義變數的格式為 “變數名=變數的值”。當在指令碼中引用變數時需要加上’$’符號,這跟前面講的在shell中自訂變數是一致的。下面看看指令碼執行結果吧。
下面我們用shell計算兩個數的和。
數學計算要用’[ ]’括起來並且外頭要帶一個’$’。指令碼結果為:
Shell指令碼還可以和使用者互動。
這就用到了read命令了,它可以從標準輸入獲得變數的值,後跟變數名。”read x”表示x變數的值需要使用者通過鍵盤輸入得到。指令碼執行過程如下:
我們不妨加上-x選項再來看看這個執行過程:
在test4.sh中還有更加簡潔的方式。
read -p 選項類似echo的作用。執行如下:
你有沒有用過這樣的命令”/etc/init.d/iptables restart “ 前面的/etc/init.d/iptables 檔案其實就是一個shell指令碼,為什麼後面可以跟一個”restart”? 這裡就涉及到了shell指令碼的預設變數。實際上,shell指令碼在執行的時候後邊是可以跟變數的,而且還可以跟多個。不妨筆者寫一個指令碼,你就會明白了。
執行過程如下:
在指令碼中,你會不會奇怪,哪裡來的$1和$2,這其實就是shell指令碼的預設變數,其中$1的值就是在執行的時候輸入的1,而$2的值就是執行的時候輸入的$2,當然一個shell指令碼的預設變數是沒有限制的,這回你明白了吧。另外還有一個$0,不過它代表的是指令碼本身的名字。不妨把指令碼修改一下。
執行結果想必你也猜到了吧。
var="Hello" #
var=Hello
var=‘Hello‘ #將忽略$等
以上變數賦值等價,
注意幾點:
1.變數和值之間不能有空格,否則解譯器會認為是幾個命令。很多程式員的習慣是在=號兩邊留空格為了好看,但這點在shell中行不通。
2.字串不必用"號或者‘,上面的幾種賦值方式是等價的。除非字串之間有空格或有變數的時候。
如:var="Hello World"
var="$a$b"
【shell指令碼中的邏輯判斷】
如果你學過C或者其他語言,相信你不會對if 陌生,在shell指令碼中我們同樣可以使用if邏輯判斷。在shell中if判斷的基本文法為:
1)不帶else
if 判斷語句; then
command
fi
在if1.sh中出現了 ((a<60))這樣的形式,這是shell指令碼中特有的格式,用一個小括弧或者不用都會報錯,請記住這個格式,即可。執行結果為:
)帶有else
if 判斷語句 ; then
command
else
command
fi
執行結果為:
3)帶有elif
if 判斷語句一 ; then
command
elif 判斷語句二; then
command
else
command
fi
這裡的 && 表示“並且”的意思,當然你也可以使用 || 表示“或者”,執行結果:
以上只是簡單的介紹了if語句的結構。在判斷數值大小除了可以用”(( ))”的形式外,還可以使用”[ ]”。但是就不能使用>, < , = 這樣的符號了,要使用 -lt (小於),-gt (大於),-le (小於等於),-ge (大於等於),-eq (等於),-ne (不等於)。
再看看if中使用 && 和 ||的情況。
shell 指令碼中if還經常判斷關於檔案屬性,比如判斷是普通檔案還是目錄,判斷檔案是否有讀寫執行許可權等。常用的也就幾個選項:
-e :判斷檔案或目錄是否存在
-d :判斷是不是目錄,並是否存在
-f :判斷是否是普通檔案,並存在
-r :判斷文檔是否有讀許可權
-w :判斷是否有寫入權限
-x :判斷是否可執行
使用if判斷時,具體格式為: if [ -e filename ] ; then
在shell 指令碼中,除了用if來判斷邏輯外,還有一種常用的方式,那就是case了。具體格式為:
case 變數 in
value1)
command
;;
value2)
command
;;
value3)
command
;;
*)
command
;;
esac
上面的結構中,不限制value的個數,*則代表除了上面的value外的其他值。下面筆者寫一個判斷輸入數值是奇數或者偶數的指令碼。
$a 的值或為1或為0,執行結果為:
也可以看一下執行過程:
case指令碼常用於編寫系統服務的啟動指令碼,例如/etc/init.d/iptables中就用到了,你不妨去查看一下。
【shell指令碼中的迴圈】
Shell指令碼中也算是一門簡易的程式設計語言了,當然迴圈是不能缺少的。常用到的迴圈有for迴圈和while迴圈。下面就分別介紹一下兩種迴圈的結構。do開始,done結束。
指令碼中的seq 1 5 表示從1到5的一個序列。你可以直接運行這個命令試下。指令碼執行結果為:
通過這個指令碼就可以看到for迴圈的基本結構 :
for 變數名 in 迴圈的條件; do
command
done
迴圈的條件那一部分也可以寫成這樣的形式,中間用空格隔開即可。你也可以試試,for i in `ls`; do echo $i; done 和 for i in `cat test.txt`; do echo $i; done
再來看看這個while迴圈,基本格式為:
while 條件; do
command
done
指令碼的執行結果為:
另外你可以把迴圈條件忽略掉,筆者常常這樣寫監控指令碼。
while :; do
command
done
【shell指令碼中的函數】
如果你學過開發,肯定知道函數的作用。如果你是剛剛接觸到這個概念的話,也沒有關係,其實很好理解的。函數就是把一段代碼整理到了一個小單元中,並給這個小單元起一個名字,當用到這段代碼時直接調用這個小單元的名字即可。有時候指令碼中的某段代總是重複使用,如果寫成函數,每次用到時直接用函數名代替即可,這樣就節省了時間還節省了空間。
fun.sh 中的sum() 為自訂的函數,在shell指令碼中要用
function 函數名() {
command
}
這樣的格式去定義函數。
上個指令碼執行過程如下:
有一點筆者要提醒你一下,在shell指令碼中,函數一定要寫在最前面,不能出現在中間或者最後,因為函數是要被調用的,如果還沒有出現就被調用,肯定是會出錯的。
Shell指令碼大體上就介紹這麼多了,筆者所舉的例子都是最基礎的,所以即使你把所有例子完全掌握也不代表你的shell指令碼編寫能力有多麼好。所以剩下的日子裡你盡量要多練習,多寫指令碼,你寫的指令碼越多,你的能力就越強。希望你能夠找專門介紹shell指令碼的書籍深入的去研究一下它。隨後筆者將給你留幾個shell指令碼的練習題,你最好不要偷懶。
1. 編寫shell指令碼,計算1-100的和;
2. 編寫shell指令碼,要求輸入一個數字,然後計算出從1到輸入數位和,要求,如果輸入的數字小於1,則重新輸入,直到輸入正確的數字為止;
3. 編寫shell指令碼,把/root/目錄下的所有目錄(只需要一級)拷貝到/tmp/目錄下;
4. 編寫shell指令碼,批量建立使用者user_00, user_01, … ,user_100並且所有使用者同屬於users組;
5. 編寫shell指令碼,截取檔案test.log中包含關鍵詞’abc’的行中的第一列(假設分隔字元為”:”),然後把截取的數字排序(假設第一列為數字),然後列印出重複次數超過10次的列;
6. 編寫shell指令碼,判斷輸入的IP是否正確(IP的規則是,n1.n2.n3.n4,其中1<n1<255, 0<n2<255, 0<n3<255, 0<n4<255)。
以下為練習題答案:
1. #! /bin/bash
sum=0
for i in `seq 1 100`; do
sum=$[$i+$sum]
done
echo $sum
2. #! /bin/bash
n=0
while [ $n -lt "1" ]; do
read -p "Please input a number, it must greater than "1":" n
done
sum=0
for i in `seq 1 $n`; do
sum=$[$i+$sum]
done
echo $sum
3. #! /bin/bash
for f in `ls /root/`; do
if [ -d $f ] ; then
cp -r $f /tmp/
fi
done
4. #! /bin/bash
groupadd users
for i in `seq 0 9`; do
useradd -g users user_0$i
done
for j in `seq 10 100`; do
useradd -g users user_$j
done
5. #! /bin/bash
awk -F‘:‘ ‘$0~/abc/ {print $1}‘ test.log >/tmp/n.txt
sort -n n.txt |uniq -c |sort -n >/tmp/n2.txt
awk ‘$1>10 {print $2}‘ /tmp/n2.txt
6. #! /bin/bash
checkip() {
if echo $1 |egrep -q ‘^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$‘ ; then
a=`echo $1 | awk -F. ‘{print $1}‘`
b=`echo $1 | awk -F. ‘{print $2}‘`
c=`echo $1 | awk -F. ‘{print $3}‘`
d=`echo $1 | awk -F. ‘{print $4}‘`
for n in $a $b $c $d; do
if [ $n -ge 255 ] || [ $n -le 0 ]; then
echo "the number of the IP should less than 255 and greate than 0"
return 2
fi
done
else
echo "The IP you input is something wrong, the format is like 192.168.100.1"
return 1
fi
}
rs=1
while [ $rs -gt 0 ]; do
read -p "Please input the ip:" ip
checkip $ip
rs=`echo $?`
done
echo "The IP is right!"
shell編程總結