腳本文件格式:
第一行,頂格:#!/bin/bash
#!/bin/bash 前面不能有任何字符或空白字符,空白行更是不行。稱之爲shebang 標記爲以bash來執行。讓內核調用解釋器來運行,而不是看成二進制來執行。node
目錄:linux
1、順序執行git
2、條件測試算法
3、算術運算shell
4、選擇執行編程
5、循環執行數組
6、數組使用
bash
7、函數app
bash腳本,面向過程的編程中
順序執行: 默認法則,逐條執行各語句。
選擇執行: 有不一樣的分支,通過條件判斷, 符合條件的分支就會執行。
循環執行: 將同一段代碼代碼反覆執行屢次, 有循環退出條件。less
1、順序執行
這個沒有什麼說的。咱們來看一下基本的腳本格式。和一些雜七雜八的東西。
第一行頂格必須是#!/bin/bash。上面咱們也說了。
#!/bin/bash # echo "Hello World"
能夠用bash命令直接執行腳本:
star@sst-pp:/tmp$ bash test.sh Hello World star@sst-pp:/tmp$
也能夠給予執行權限,再直接執行:
star@sst-pp:/tmp$ chmod +x test.sh star@sst-pp:/tmp$ ./test.sh Hello World star@sst-pp:/tmp$
命令引用:
把命令替換成命令的執行結果。
#!/bin/bash # echo "Hello World" echo "This time is `date +%F-%T`" #這裏是鍵盤上tab鍵上面的那個` echo "This time is $(date +%F-%T)" #命令引用還能夠用$()的方式。
執行結果:
star@sst-pp:/tmp$ ./test.sh Hello World This time is 2016-02-12-21:03:14 This time is 2016-02-12-21:03:14 star@sst-pp:/tmp$
注意:在當前的執行環境中,正在執行的命令和在命令引用裏的命令是不同的。如:
star@sst-pp:~/mydata/linux$ date +%F 2016-02-12 star@sst-pp:~/mydata/linux$ `date +%F` 2016-02-12:未找到命令 star@sst-pp:~/mydata/linux$
在命令引用出來之後就是命令的結果,固然若是結果碰巧也是能夠執行的命令那就另外一說了。瞭解一下,一下子在if語句那裏再提一下。
變量引用和命令引用都是用來獲取數據的。$abc 獲取abc的數據,$(cat /etc/passwd) 獲取cat命令的執行結果。
雙引號,單引號:
而這裏的雙引號是用來把字符串括起來,否則中間有空格的話,傳給echo的就只是一部分字符了。
在bash裏面單雙引號是不一樣的。
單引號:強引用,單引號裏面的數據徹底成了普通字符串,bash不會過多處理它。
雙引號:弱引用,雙引號裏面的數據除了空格之外,其它的特殊字符,bash都會處理。
#!/bin/bash # echo 'This time is `date +%F-%T`' #單引號 echo "This time is $(date +%F-%T)" #雙引號 echo "$$" #顯示當前shell的PID。 能夠不加雙引號。 echo '$$' #這裏用的單引號。
執行結果:
star@sst-pp:/tmp$ ./test.sh This time is `date +%F-%T` This time is 2016-02-12-21:25:52 3014 $$ star@sst-pp:/tmp$
單引裏面的變量引用和命令引用都不能使用,其它的特殊字符也同樣。
位置參數:
#!/bin/bash echo "$1-$2-$3" #位置變量 echo "total:$*" #總的參數 echo "count:$#" #參數數量 echo "path:$0" #執行路徑 echo "script_name:`basename $0`" #腳本名稱
執行結果:
star@sst-pp:/tmp$ ./test.sh 1 3 5 7 9 10 1-3-5 total:1 3 5 7 9 10 count:6 path:./test.sh script_name:test.sh star@sst-pp:/tmp$
2、條件測試:
若是變量有可能會爲空,必定要加上雙引號,在任何測試中,否則爲報:須要一元表達式的錯誤。
測試方法
test EXPRESSION
[ EXPRESSION ]
` EXPRESSION `
EXPRESSION爲測試表達式,把用於測試條件的表達式放在那個位置就能夠,它會返回0或1。再由if或while之類的語句作出判斷。在bash裏面0爲真,非0爲假。
注意:EXPRESSION兩端必須有空白字符,不然爲語法錯誤;
任何命令的執行結果均可以作爲判斷條件。用命令的狀態返回值作判斷。命令會返回0或非0。
經常使用判斷類型:
整數測試: 比較大小。
字符測試: 比較字符串大小, 是否爲空。
文件測試: 文件類型,文件是否存在。
(1)字符測試
雙目測試符:
> 大於
< 小於
== 等於,等值比較。有時用一個=號也能夠,可是爲了規範,仍是用兩個。等號兩邊有空格
[ "stringVarA" == "stringVarB" ]
=~ 左側是字符串或變量,右側是一個模式,斷定左測的字符串可否被右側的模式所匹配,一般只能在[[]]雙中括號中使用。字符測試中的=~模式匹配要在[[]]裏面。測試發現 \< \> 單詞錨定不可用, 不會報錯, 但返回值是不正確的。模式不要加引號。
[[ "strinsA" =~ cd ]] 用來測試變量strinsA中是否有cd這個字符串。
字符測試中的字符串或變量最好要加上引號,用來肯定這個裏面就是數據,避免若是是空變量的話,測試結果會不同。
!=,<> 都是不等於的意思。通常都用!=
在字符測試中有時候要用雙中括號,否則會報相似的信息。
user.sh: 第 6 行: [: ==: 須要一元表達式
單目測試符:
-n "$stringVar" 判斷字符串或變量是否不空。這個其實只是判斷後面是否有數據,就算是命令返回的數據也同樣。只不過命令要放在命令引用裏面。
[ -n "$stringVar" ] 判斷變量是否不空。不空爲真,空爲假。
-z "$stringVar" 判斷字符串或變量是否爲空。空爲真,不空爲假。
注意: 變量必定要加上雙引號, 由於變量若是爲空,沒有引號就是沒有指定給判斷符爲空的值。這樣的狀況最後結果會是變量不空。
(2)數值測試:
-gt 大於 greater than
-lt 小於 less than
-ge 大於等於
-le 小於等於
-ne 不等於
-eq 等於 equal to
使用方式都同樣。[ $num1 -eq $num2 ]
(3)文件測試:
單目測試:
-e file : 測試文件是否存在。 存在則爲真,不存在則爲假。
-a file : 也是測試文件是否存在。 存在則爲真,不存在則爲假。
-f file : 測試文件是否爲普通文件
-d file : 測試文件是否爲目錄文件
-b file : 測試文件是否爲塊設備
-c file : 測試文件是否爲字符設備
-h file : 測試文件是否爲符號連接文件。 -L 也是一樣的功能
-p file : 測試文件是否爲管道文件。
-S file : 測試文件是否爲套接字文件。 大寫的S
-s file : 測試文件是否不空。
-r file : 測試執行用戶是否對文件有讀取權限。
-w file : 測試執行用戶是否對文件有寫權限。
-x file : 測試執行用戶是否對文件有執行權限。
測試文件的時候 不要在文件右邊加上斜槓,如 file/,這樣會把file/ 看成一個文件,而不是file. 因此就老是假。
雙目測試:
-nt -ot 中第二個文件若是不存在,也會認爲第一個文件更新或更老, 也就是結果爲真。
file1 -nt file2 測試是否第一個文件比第二個文件新。
file1 -ot file2 測試是否第一個文件比第二個文件老。
file1 -ef file2 測試兩個文件是否在相同的設備而且相同的inode號。
3、算術運算
算術運算,用來把字符變量進行算術運算。最終把字符的結果,如:23+3 變成數值的結果。否則的話:
[root@nfs ~]# a=5 [root@nfs ~]# b=4 [root@nfs ~]# c=a+b [root@nfs ~]# echo $c a+b [root@nfs ~]# c=$a+$b [root@nfs ~]# echo $c 5+4
都是字符相加了。
算術運算方式:
declare
-i 明確聲明爲整型變量,能夠直接算術運算, a=$b+$c
只要把存儲結果的變量聲明爲整型就能夠。 上面的a.
let 指明爲算術運算
let varName=算術表達式 , let sum=$a+$b
不能直接顯示出結果,必須以這種把值賦給前面的變量的方式來顯示結果。
let 只是讓後面的變量以×××計算。而至於結果給誰,怎麼顯示let都不會管。
因此咱們須要手動把結果給一個存儲空間,在這裏也就是須要賦給變量了。
若是計算結果存在小數, 將會被圓整。
varName=$[算術表達式] 也能夠把中括號換成兩個小括號 (())
能夠直接計算出數值,因此能夠像下面這樣直接用echo 顯示出結果。
如: echo $[$a+$b]
$[$a+$b] 其實就至關於 $[12+23]
$[$a+$b]寫成$[a+b]也是能夠的。
expr 來指定進行算術運算。 變量與中間的 運算符 必需要有空格。
varName=`expr $num1 + $num2`
這裏是直接用的命令引用,因此這個是直接計算出結果之後再給變量varName的。
expr $num1 + $num2 就能夠直接得出結果。
由於expr是一個命令,後面的數值和運算符都是參數,因此纔要空格分開。
算術運算操做符:
+ | 加 |
- | 減 |
* |
乘 |
/ |
除 |
% |
取餘 |
** |
乘方 |
取餘運算能夠用來判斷是否奇偶數, 與2 得0 也就是偶數,與2得1也就是奇數
注意:有些時候乘法符號可能須要轉義,由於它是通配符啊。
加強型賦值:
變量作某種算術運算後回存至此變量中;#表示數字。
通常是這樣: let i=$i+#
如今是這樣: let i+=#
+=,-=,*=,/=,%=
自加,自減,自乘,。。。。。
自增:
VAR=$[$VAR+1]
let VAR+=1
let VAR++
自減:
VAR=$[$VAR-1]
let VAR-=1
let VAR--
若是老是在自身的基礎上+=1 就能夠用 ++ 來表示。
let varName++ 用 let 算術運算++。
若是隻是單純的++,還能夠用(())來替換let,注意不是$(())啊。
((varName++))
額外:
++的方式有兩種,一是在變量後面++ 一種是在變量前面++
++varName 在前面的是先加1再引用。 後面的是先引用再加1.
正常給變量賦值可能看不出來什麼, 由於咱們使用的只是變量。 可是:
看這個:
[root@Test ~]# a=
[root@Test ~]# echo $[a++]
0
[root@Test ~]# echo $a
1
[root@Test ~]#
很明顯是算術表達式 $[] 在運算以前,先給了echo 0 。
而後纔給a加了1.
那麼若是把++放在前面:
[root@Test ~]# a=
[root@Test ~]# echo $[++a]
1
[root@Test ~]# echo $a
1
[root@Test ~]#
這就是先由算術表達式$[]給a進行了++ 運算,而後再把值給echo。
上面的$[]若是換成$(())也是同樣的。 它們都是算術表達式。
腳本執行:
bash -x 顯示腳本的執行過程,用於調試。bash -n 檢測有沒有語法錯誤。
4、選擇執行:
(1) &&, ||
在邏輯運算裏面。若是兩個條件作與運算,那麼只要第一個條件爲假,總體的運算結果必定爲假。
而作或運算,只要第一個條件爲真,那麼總體必定也爲真。
&&爲與運算符, || 爲或運算符。
短路法則:
~]# COMMAND1 && COMMAND2
COMMAND1爲「假」,則COMMAND2不會再執行;
不然,COMMAND1爲「真」,則COMMAND2執行;
~]# COMMAND1 || COMMAND2
COMMAND1爲「真」,則COMMAND2不會再執行;
不然,COMMAND1爲「假」,則COMMAND2執行;
例如:
#!/bin/bash # id $1 &> /dev/null && echo "$1 already exis" || useradd $1
執行過程:
star@sst-pp:/tmp$ sudo ./test.sh root root already exis star@sst-pp:/tmp$ sudo ./test.sh sst star@sst-pp:/tmp$ sudo ./test.sh sst sst already exis star@sst-pp:/tmp$
若是id的結果爲真,則會執行與運算符後面的操做。而這裏只是echo數據,結果也爲真。那麼與運算結果爲真, 或運算只要運算符前面爲真,則不會再執行後面的操做。
而若是id爲假,與運算符後面的操做也不會再執行。 與運算總體爲假, 或運算符前面爲假,開始執行或運算符後面的操做。
這裏有個小坑:
#!/bin/bash # id $1 &> /dev/null && echo "$1 already exis" || useradd $1 && echo "$1 create success"
執行過程:
star@sst-pp:/tmp$ sudo ./test.sh root root already exis root create success star@sst-pp:/tmp$ sudo ./test.sh user1 user1 create success star@sst-pp:/tmp$ sudo ./test.sh user1 user1 already exis user1 create success star@sst-pp:/tmp$
由於最後一個與運算符前面的條件始終都會爲真,因此後面的操做每次都會執行。
若是id爲真,第一個與運算符後面的echo也爲真,這個與運算結果爲真。而後與後面的或運算符再運算,雖然或運算符後面的useradd不會執行,但或運算結果爲真。
就算是沒有最後一個與運算,而前面的與和或換一下位置也會發生這種狀況。
id $1 &> /dev/null || useradd $1 && echo "$1 already exis"
最後再來一個:
#!/bin/bash # [ -r /tmp/var.sh ] && source /tmp/var.sh && echo -e "$user\n$file\n$shell"
執行結果:
star@sst-pp:/tmp$ ./test.sh root ab.txt /bin/bash star@sst-pp:/tmp$
從另外一個文件中讀入了變量。
(2) if語句
if語句有三種格式:
單分支:
if 條件; then
分支1;
fi
雙分支:
if 條件; then
分支1;
else #不知足條件,執行這個。
分支2;
fi
多分支:
if 條件1; then
分支1;
elif 條件2; then
分支2;
elif 條件3; then
分支3;
.....
else #上面都不知足的時候,執行這個分支。也能夠不寫。
分支n;
fi
注意:即使多個條件可能都能同時知足,也只是會執行其中第一個知足條件的。
例:經過命令行參數給定一個用戶名,若是用戶存在則輸出用戶名與shell。
#!/bin/bash if id $1 &> /dev/null;then echo "User: $1" echo "Shell: `grep $1 /etc/passwd | cut -d : -f 7`" fi
執行過程:
star@sst-pp:/tmp$ ./test.sh abc star@sst-pp:/tmp$ ./test.sh root User: root Shell: /bin/bash star@sst-pp:/tmp$ ./test.sh nobody User: nobody Shell: /usr/sbin/nologin star@sst-pp:/tmp$
咱們知道,測試一個條件也能夠用命令的執行狀態,上面用&>重定向把命令的全部輸出結果重定向到/dev/null。if只要這個命令的狀態碼就夠了。
注意: 不要加上命令引用,命令引用獲取的是命令的輸出結果,這樣if判斷的就是這個命令的輸出的數據了。如: if `id root` 執行的時候就變成 if uid=0(root) gid=0(root) 組=0(root) 。
例:經過命令行參數給定一個用戶名,判斷其ID號是偶數仍是奇數;
#!/bin/bash # uid=`id -u $1 2> /dev/null` [ -z "$uid" ] && echo "no user $1" && exit 1 if [ $[$uid%2] -eq 1 ];then echo "$1 uid is Odd number" else echo "$1 uid is Even number" fi
執行過程:
star@sst-pp:/tmp$ ./test.sh root root uid is Even number star@sst-pp:/tmp$ ./test.sh sst sst uid is Odd number star@sst-pp:/tmp$ ./test.sh eer no user eer star@sst-pp:/tmp$
上面的2>是錯誤重定向,把原本應該輸出到屏幕的重定向到/dev/null。下面再細說重定向。
exit退出腳本,而且腳本的返回值爲後面所指定的值。 通常狀況下沒有exit,腳本的返回值會是最後一個命令的狀態值。
例:經過命令行參數給定兩個文本文件名,若是某文件不存在,則結束腳本執行;都存在時返回每一個文件的行數,並說明其中行數較多的文件;
#!/bin/bash # [ $# -lt 2 ] && echo "need more file path" && exit 1 #if all file not exis,elif single file not exis, if ! [ -f $1 -o -f $2 ];then echo "$1 and $2 is not find" exit 1 elif ! [ -f $1 ];then echo "$1 is not find" exit 1 elif ! [ -f $2 ];then echo "$2 is not find" exit 1 fi #line number file1=`wc -l $1 | cut -d' ' -f1` file2=`wc -l $2 | cut -d' ' -f1` if [ $file1 -gt $file2 ];then more=$1 elif [ $file1 -lt $file2 ];then more=$2 else more="same" fi echo "`basename $1` line is: $file1" echo "`basename $2` line is: $file2" echo "more: $more"
執行:
star@sst-pp:/tmp$ ./test.sh /etc/passwd /etc/fsta /etc/fsta is not find star@sst-pp:/tmp$ ./test.sh /etc/passwd /etc/fstab passwd line is: 45 fstab line is: 21 more: /etc/passwd star@sst-pp:/tmp$
I/O重定向:這裏就只是簡單描述一下了。
I/O 重定向(輸入輸出重定向)
輸入重定向 : <
輸出重定向: > , >>
錯誤重定向: 2> , 2>>
全部輸出 &> , &>>
> * 2>&1 , >> * 2>&1
這裏是把錯誤輸出加到標準輸出中。也能夠把標準輸出加入到錯誤輸出中。
2> * 1>&2
&是專門用來引用文件描述符的, 也能夠手動爲某個文件設定自定義的文件描述符。
/dev/null 黑洞,常在重定向中使用。 重定向到些位置的數據都會清除。還有一個這裏順便提一下/dev/zero,泡泡機,經常使用於生成虛擬磁盤文件。以0填充生成文件,沒有實際數據。
<< 能夠用於在腳本中建立文件,或者生成菜單。 也算是輸入重定向。
如:
star@sst-pp:/tmp$ cat > test << EOF > Hello World > +++++ > ----- > good luck > EOF star@sst-pp:/tmp$ cat test Hello World +++++ ----- good luck star@sst-pp:/tmp$
這裏的過程是<<先把數據給了cat,cat正常狀況下是要輸出到屏幕的,這裏重定向到了文件。
cat > test這是一部分,<<EOF是另外一個部分,EOF是用於結束輸入的,這個字符能夠自定,通常都用EOF來表示。
上面說了,通常是用來在腳本中生成顯示菜單的。如:
star@sst-pp:/tmp$ cat << EOF > Hello > World > good luck > EOF Hello World good luck star@sst-pp:/tmp$
輸入的數據會由cat標準輸出到屏幕上。
中間的數據也能夠用變量表示,而完成自動向文件輸入數據或輸出到屏幕。
(3) case語句
case $VARAIBLE in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
分支n
;;
esac
case支持glob風格的通配符:
*:任意長度的任意字符;
?:任意單個字符;
[]:範圍內任意單個字符;
a|b:a或b;
也支持一些特定的字符集:
[:upper:] 大寫字母 [:lower:] 小寫字母
[:alpha:] 全部字母 [:digit:] 全部數字
[:alnum:] 全部字母和數字 [:space:] 空格
[:punct:] 標點符號
這些只是表明一組特定的字符,使用的時候要在[]裏面。如:[[:upper:]]。
命令補充:交互式腳本
read
-p 能夠添加提示符 如: read -p "Please Enter number" num
-t 超時時間。
數據比變量多的話, 多出的數據會賦給最後一個變量。
變量比數據多的話, 多出的變量爲空。 就算之前有值,也會變成空。
case不能像if同樣作很複雜的判斷,它只是簡單的匹配,匹配成功則執行對應的分支。
適用於有不少選擇,但不用作複雜的判斷的狀況。if也能夠作不少選擇判斷,不過就有點臃腫了。
例:顯示一個菜單讓用戶選擇,經過不一樣的選擇作出不一樣的操做。
#!/bin/bash # cat << EOF 1)show cpu info(c) 2)show disk partition info (d) 3)show disk use (s) 4)show memrofy info (m) 5)Quit(q) EOF read -p "Enter your choose:" option #if option is null, default m option=${option:-m} #若是輸入的是大寫字母,把它改成小寫。也能夠用option=${option,}來實現,不過不經常使用。 option=`echo $option | tr [[:upper:]] [[:lower:]]` case $option in c) lscpu ;; d) fdisk -l ;; s) df -Th ;; m) free -m ;; *) exit 0 esac
執行:
star@sst-pp:/tmp$ sudo bash info.sh 1)show cpu info(c) 2)show disk partition info (d) 3)show disk use (s) 4)show memrofy info (m) 5)Quit(q) Enter your choose:q star@sst-pp:/tmp$
數據量太大了,就不貼了。 最後的*)至關因而替補了,由於*是匹配全部的,上面匹配不到的,就到這裏執行來了。
5、循環執行
循環執行: 將一段代碼重複執行0、1或屢次;
進入條件:條件知足時才進入循環;
退出條件:每一個循環都應該有退出條件,以有機會退出循環;
for循環
while循環
until循環
一、for循環
兩種格式:
(1) 遍歷列表
(2) 控制變量
遍歷列表:
for VARAIBLE in LIST; do
循環體
done
進入條件:只要列表有元素,便可進入循環;
退出條件:列表中的元素遍歷完成;
列表的生成
{start..end} 如:{1..10} 生成1-10的列表。可是裏面只能用數字,而不能用變量。
seq [起始] [步長] 結束 起始默認爲1 步長默認爲1.
如: seq 6 16 生成從6到16的數字列表,能夠指定步長。 seq 1 2 6 得出: 1 3 5
列表不僅是輸入的數據,也能夠是文件名,因此可使用文件名通配符的方式表明目錄下邊的全部文件,這樣每個文件名均可以賦給變量。
使用命令生成, 如 ls 出來目錄下的全部文件, 可是若是文件名有空格就麻煩了。
直接手動給出列表, 每一個值用空格分開。
列表中的數據是以空格或tab或換行分開的。
例:求100之內全部正整數之和。
#!/bin/bash # sum=0 for i in {1..100};do let sum+=$i done echo $sum
執行:
star@sst-pp:/tmp$ bash sum.sh 5050 star@sst-pp:/tmp$
那若是是任意數之間的全部正整數之和呢,只要1和100用變量替換就能夠啦。
例:計算當前系統上的全部用戶的uid和gid之和。直接以命令取出/etc/passwd文件中的uid與gid。
#!/bin/bash # sum_uid=0 sum_gid=0 file=/etc/passwd for i in `cut -d: -f3 $file`;do let sum_uid+=$i done for i in `cut -d: -f4 $file`;do let sum_gid+=$i done echo "uid sum is: $sum_uid" echo "gid sum is: $sum_gid"
結果:
star@sst-pp:/tmp$ bash sum_id.sh uid sum is: 72346 gid sum is: 334031
例:再來個有意思的,輸出99乘法表。
#!/bin/bash for i in {1..9};do #這個是控制豎列的從1到9。 for y in `seq 1 $i`;do #這個是控制一行從1到幾的。這個幾就是上面i的值。 echo -n -e "${y}x${i}=$[$y*$i]\t" done echo done
結果:
star@sst-pp:/tmp$ bash 9x9.sh 1x1=1 1x2=2 2x2=4 1x3=3 2x3=6 3x3=9 1x4=4 2x4=8 3x4=12 4x4=16 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81
若是再修改一下:
#!/bin/bash for i in {1..9};do #這裏和上面同樣。 for y in `seq 1 $i`;do echo -n -e "${y}x${i}=$[$y*$i]\t" done echo done #下面是逆序的99乘法表。 for i in {1..9};do #這裏也是從1到9的循環。 count=$[9-$i] #由於是倒着來的,因此這裏就用9減去i。 for y in $(seq 1 $count);do #表明每一行的列表就是愈來愈小了。 echo -n -e "${y}x$count=$[$y*$count]\t" done echo done
結果:
star@sst-pp:/tmp$ bash 9x9.sh 1x1=1 1x2=2 2x2=4 1x3=3 2x3=6 3x3=9 1x4=4 2x4=8 3x4=12 4x4=16 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 1x9=9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81 1x8=8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64 1x7=7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49 1x6=6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36 1x5=5 2x5=10 3x5=15 4x5=20 5x5=25 1x4=4 2x4=8 3x4=12 4x4=16 1x3=3 2x3=6 3x3=9 1x2=2 2x2=4 1x1=1
for循環的特殊用法:控制變量
for ((控制變量初始化;條件判斷表達式;控制變量的修正語句)); do
循環體
done
控制變量初始化:僅在循環代碼開始運行時執行一次;
控制變量的修正語句:每輪循環結束會先進行控制變量修正運算,然後再作條件判斷;
例:仍是小99:
#!/bin/bash for ((i=1;i<=9;i++));do for ((y=1;y<=i;y++));do echo -n -e "${y}x${i}=$[$y*$i]\t" done echo done for ((i=8;i>=1;i--));do for ((y=1;y<=i;y++));do echo -n -e "${y}x${i}=$[$y*$i]\t" done echo done
結果與上面同樣。
不過我以爲這種方式通常仍是用在數據從大減到小的狀況時。
for ((y=$soft_num;y!=0;y--));do
而正常狀況下的列表方式,是從小增到大。
while循環:
while CONDITION; do
循環體
循環控制變量修正表達式
done
進入條件:CONDITION測試爲」真「
退出條件:CONDITION測試爲」假「
例:仍是求100之內正整數之和。
#!/bin/bash sum=0 num=1 while [ $num -le 100 ];do let sum+=$num ((num++)) done echo $sum
結果就是5050,這裏不貼了。
例:上面的顯示信息的腳本加上循環,只有輸入quit的時候纔會退出循環。
#!/bin/bash option=m while [ $option != quit ];do cat << EOF 1)show cpu info(c) 2)show disk partition info (d) 3)show disk use (s) 4)show memrofy info (m) 5)quit EOF #這個必須頂格 read -p "Enter your choose:" option option=${option:-m} option=${option,} case $option in c) lscpu ;; d) fdisk -l ;; s) df -Th ;; m) free -m ;; esac done
還要在while以前重複定義option,好像有點多餘,但上面的條件中用的option又不能爲空。
能夠用true來表示條件,表示真。一直都是真。 而退出循環就要經過break來了。
循環控制語句:
continue:提早結束本輪循環,而直接進入下一輪循環判斷;
break:提早跳出循環。
注意: break是跳出循環的,後面能夠加上數字,表示跳出幾層循環(在循環嵌套中)。而exit是退出腳本的。
如寫成這樣就能夠了。
#!/bin/bash while true;do cat << EOF 1)show cpu info(c) 2)show disk partition info (d) 3)show disk use (s) 4)show memrofy info (m) 5)quit EOF read -p "Enter your choose:" option option=${option:-m} option=${option,,} [ $option == quit ] && break ## case $option in c) lscpu ;; d) fdisk -l ;; s) df -Th ;; m) free -m ;; esac done
while循環的特殊用法(遍歷文件的行):
while read VARIABLE; do
循環體;
done < /PATH/FROM/SOMEFILE
依次讀取/PATH/FROM/SOMEFILE文件中的每一行,且將值賦值給VARIABLE變量;
例:輸出/etc/passwd文件中shell爲/bin/bash的用戶。
#!/bin/bash # while read line;do user=`echo $line | cut -d: -f1` shell=`echo $line | cut -d: -f7` if [[ $shell == /bin/bash ]];then echo -n "$user " fi done < /etc/passwd ## echo
star@sst-pp:/tmp$ bash user.sh root star sstc
命令的輸出也能夠通過管道傳給while。如把上面腳本中done後面的</etc/passwd去掉之後。
用管道傳遞的方式,效果也是同樣的。
cat /etc/passwd | while read line;do .....
而在執行腳本的時候,也是能夠直接傳遞的。
star@sst-pp:/tmp$ cat /etc/passwd | bash user.sh root star sstc
until循環:
until CONDITION; do
循環體
循環控制變量修正表達式
done
進入條件:CONDITION測試爲」假「
退出條件:CONDITION測試爲」真「
until跟while結構同樣。只不過這個是爲假才執行,爲真就退出。
6、數組使用:
直接來例子:想從一些學生中隨機的挑出幾個。
#!/bin/bash #discrition: this is random print student name list modu=10 #要與人數相同 count=4 #挑出幾個 student=('liMing' 'xisoHui' 'Hua' 'Mals' 'Bili' 'youo' 'dAn' 'zhangsan' 'lisi' 'xiaoli') #學生列表 for i in `seq 1 $count`;do random=$[$RANDOM%$modu] #生成隨機的數字。$RANDOM是會生成隨機數的變量。 與總人數取餘,就能夠得出從0到總人數減1之間的隨機數。 echo "${student[$random]}" #顯示數組中對應的隨機索引的學生。 student[$random]=${student[$modu-1]} #把已經顯示過的學生直接用最後一個學生覆蓋。 let modu-- #總人數減一。 done
最後兩句再解釋一下:好比上面是要挑出4個學生,也就要執行四次循環,結果頗有可能會在4次循環中顯示出相同的學生。 而要讓顯示過的學生下次循環確定不會再顯示,我這裏就是用最後一個學生把顯示過的學生覆蓋,而後下次循環的時候,隨機索引就再也不包含最後一個學生。
執行結果:
star@sst-pp:~/script$ bash array1.sh zhangsan lisi Hua xiaoli
這種狀況若是用每一個變量保存一個學生名稱就有點費勁了,並且也也太不靈活了。在數組裏面能夠直接在後面加學生,並把總人數改爲對應值便可。
例:有時候在網上下載了一個軟件,在系統上計算出效驗碼之後與軟件所提供的作對比,費眼啊。
#!/bin/bash # describe: compare file md5/sha1 and appoint md5/sha1 same. # default -c sha1 # exit 1 par error # exit 3 -k error # exit 4 can't find md5sum or sha1sum # exit 5 can't find -f file [ $# -lt 2 ] && echo "apr too little" && echo "Usage:`basename $0` [-c md5|sha1] -k key_code -f path_file" && exit #取參數 while [ $# -ge 2 ];do case $1 in -c) soft=$2 shift 2 ;; -k) key=$2 shift 2 ;; -f) file=$2 shift 2 ;; *) echo "Usage:`basename $0` [-c md5|sha1] -k key_code -f path_file" exit 1 esac done soft=${soft:-sha1} case $soft in md5) soft=md5sum ;; sha1) soft=sha1sum ;; *) echo "-c md5|sha1" exit 3 esac [ ! which $soft &> /dev/null ] && echo "Can't find software $soft command" && exit 4 [ ! -e "$file" ] && echo "Can't find file $file" && exit 5 #取出變量中字符的長度 key_len=`echo ${#key}` #把-k帶入變量中的字符依次賦值給數組。其實能夠直接用變量中的字符依次比較,而不用數組。 for i in `seq 0 $[$key_len-1]`;do key_arr[$i]=${key:$i:1} done #計算文件的校驗碼. file_key=`$soft $file | cut -d' ' -f1` file_key_len=`echo ${#file_key}` #把文件的校驗碼依次賦值給數組。其實能夠直接用變量中的字符依次比較,而不用數組。 for i in `seq 0 $[$file_key_len-1]`;do file_key_arr[$i]=${file_key:$i:1} done [ $file_key_len -ge $key_len ] && count=$file_key_len || count=$key_len #肯定輸出顏色,並先打印出-k帶來的校驗碼。 for i in `seq 0 $[$count-1]`;do if [ "${key_arr[$i]}" == "${file_key_arr[$i]}" ];then color[$i]=2 else color[$i]=1 fi echo -n -e "\033[3${color[$i]}m${key_arr[$i]}\033[0m" done echo for i in `seq 0 $[$count-1]`;do echo -n -e "\033[3${color[$i]}m${file_key_arr[$i]}\033[0m" done echo
執行結果:
這裏用到了shift。
位置參數輪替(shift)
輪替的意思就是 全部位置參數向後移,連$#也是如此,
輪替完之後 位置參數至關於就再也不計算前面的參數了。把前面的忽略了。
shift 默認爲1個位置輪替,
能夠指定 如: shift 2
echo顏色與相關:
echo -e 用來支持控制字符 如, \n 換行 \b 退格 \t 製表符
還能夠控制特殊的輸出方式。
"\0NNN[##m數據\0NNN[0m"
最後的\033[0m 是用來取消屬性的。 否則這個屬性會應用到echo之外去。
裏面的##
第一個是表示顏色前景或是背景,或是字體格式,如閃爍。
若是第一個是表示的顏色的話,第二個就是具體什麼顏色。
如: echo -e "\033[32mHello\033[0m"
若是隻有一個#號位:
[5 是跳躍
[7 是前景背景調換
[1 是加粗
[4 是加下劃線
若是有兩個#號:
[3 是前景色
[4 是背景色
第二個#號:
0黑,1紅,2綠,3黃,4藍,5紫,6天藍,7灰。
若是有多個參數要用,能夠用 ; 隔開。
echo -e "\033[32;5;1mHello\033[0m"
前景綠色;跳躍;加粗
reset 能夠重啓終端, 不當心把參數用到外面來了,用這個恢復默認。
其它的能夠控制顏色的命令也能夠恢復,如再顏色選項的ls。
echo -n 不換行。
例:實現冒泡算法。隨機生成幾個數。從小到大排序。
#!/bin/bash #description: # total_num=8 index_num=$[$total_num-1] soft_num=$index_num liushi=0 for i in `seq 0 $index_num`;do num_arr[$i]=`echo $RANDOM` done while [ $soft_num -ne 0 ];do for i in `seq 0 $[$soft_num-1]`;do if [ ${num_arr[$i]} -gt ${num_arr[$i+1]} ];then linshi=${num_arr[$i+1]} num_arr[$i+1]=${num_arr[$i]} num_arr[$i]=$linshi fi done ((soft_num--)) done for i in `seq 0 $index_num`;do echo ${num_arr[$i]} done
結果:
star@sst-pp:~$ bash ooooo.sh 467 2321 2831 7015 9663 15704 29946 31867 star@sst-pp:~$
最外層的while循環是控制總體循環次數,裏層的for是負責把大的數字移到數組後邊的索引位置。而循環次數不用每次都同樣,由於最大的數已經在最上邊了,很明顯它也不用再移動了。
7、函數:
函數:function
過程式編程:代碼重用
注意:定義函數的代碼段不會自動執行,在調用時執行;所謂調用函數,在代碼中給定函數名便可;函數名出現的任何位置,在代碼執行時,都會被自動替換爲函數代碼;
函數名也是指向內存的地址,只不過這個地址存儲的是代碼。
語法:兩種格式
FuncName(){
函數體
}
函數名和括號之間,括號和大括號之間有沒有空格均可以。
function FuncName {
函數體
}
函數能夠接受參數:
傳遞參數給函數:
在函數體中當中,可使用$1,$2, ...引用傳遞給函數的參數;還能夠函數中使用$*或$@引用全部參數,$#引用傳遞的參數的個數;
在調用函數時,在函數名後面以空白符分隔給定參數列表便可,例如,testfunc arg1 arg2 arg3 ...
函數返回值:
函數的執行結果返回值:
(1) 使用echo或printf命令進行輸出;
(2) 函數體中調用的命令的執行結果;
函數的退出狀態碼:
(1) 默認取決於函數體中執行的最後一條命令的退出狀態碼;
(2) 自定義:return
return # 指定狀態返回值,退出函數。
跟exit # 功能相似,只不過是退出函數而已。
若是在函數中用exit 也是直接退出腳本。
函數變量做用域:
全部的變量默認狀況下都是屬於主程序的,而且都是字符型的,雖然沒有聲明過,雖然沒有賦值。這個是由於bash的緣由。這種的變量都是本地變量。會向下傳遞變量,因此會把變量傳遞給函數,函數可使用也能夠修改。這樣的狀況下,在函數中給同一個變量賦值會修改變量在主程序中的值,因此在函數中最好使用local來指定局部變量。
local 來指定是局部變量。把變量名和數據放到本身的內存空間存放,不把變量和數據放到主程序所擁有的內存空間。這樣無論變量在主程序中有沒有值,都是不會變的。
在函數中聲明的本地變量,在主程序中不可使用,由於在函數中聲明的變量,是屬於這個函數的,只能夠向下傳遞,可是不能夠向上把變量傳遞給主腳本, 能夠向下傳遞給子函數。也不會修改主腳本變量的已有的值。只要聲明這個變量,那麼它們的所指定的內存空間地址就不同。不能傳遞給子shell
若是隻是在函數中給變量賦值,那麼主程序可使用這個變量。由於只要是沒有指定在函數中聲明,這個變量就是屬於被主程序聲明過的, 並且仍是字符型的。誰聲明變量,變量就在誰的內存空間中,就算是都聲明瞭一樣的變量,那也是會在各自的內存空間,互不幹攏。
環境變量只有跨shell纔會失效,子shell有效。因此在腳本中聲明的環境變量在腳本以外就不能用了
位置變量。 位置變量不屬於腳本, 只是簡單的給腳本傳遞參數,因此,腳本的位置變量,在函數中是不可使用的。不過函數也是有位置參數的,只不過是腳本調用函數的時候指定傳遞給函數的。
如: functionName Parameters1 Parameters2 ....
而且能夠間接的把腳本的位置參數傳遞給函數。
functionName $1 $2
functionName $*
把腳本的位置變量的數據傳遞給函數
注意上邊是把腳本位置變量的數據,而不是把位置變量直接傳遞
例:
#!/bin/bash test () { echo $1 $2 echo $* echo $# } test a b c d e
結果:
star@sst-pp:/tmp$ bash fun.sh a b a b c d e 5 star@sst-pp:/tmp$
例:n*n錶帶上顏色。指定從幾乘到幾,而且加上顏色。
#!/bin/bash # first=$2 first=${first:-0} #這個函數就是爲了生成一個1-7之間隨機的返回值,讓後面的echo來使用。從而也就有了隨機的顏色了。 color () { col=0 while [ $col -eq 0 ];do #由於0是黑色,黑色的窗口顯示不出來。因此只要是0就從新生成隨機值。 col=$[$RANDOM%8] done return $col } #need multiply number(second number) nxn () { number=$1 for i in $(seq $first $number);do color echo -e -n "\033[3${?}m${i}x${number}=$[$i*$number]\033[0m\t" done } for i in $(seq $first $1);do nxn $i echo done co=$[$1-1] while [ $co -ge $first ];do nxn $co echo ((co--)) done
結果:
前一個參數是乘到幾結束,後面的參數是從幾開始。
例:ping 172.16.網段*.1的主機是否在線。
#!/bin/bash # # trap "echo 'quit';exit" INT #信號捕捉,否則的話,咱們的ctrl+c的信號極可能是關不了腳本的。 address="172.16" up_count=0 down_count=0 opration () { #這個函數只是負責接受IP地址,而後PING,再根據狀態用不一樣的顏色顯示出來。 ping -W 1 -c 1 $1 &> /dev/null && state=0 || state=1 if [ $state -eq 0 ];then echo -e "\033[32m$1\tis up\033[0m" ((up_count++)) else echo -e "\033[31m$1\tis down\033[0m" ((down_count++)) fi } for i in {0..255};do opration "$address.$i.1" done opration "$address.67.1" echo "$up_count is up" echo "$down_count is down"
結果:
信號捕捉:只能在腳本的最前面。
列出信號:
trap -l
kill -l
trap 'COMMAND' SIGNALS
常能夠進行捕捉的信號:HUP, INT
man 7 signal 查看信號幫助。
不能捕捉kill(9) 和 term(15) 信號。
trap在腳本開始就開始運行,等待信號。如今一些信號由trap來處理,也就不會再發生關不掉腳本的狀況了。如像上面的腳本,若是沒有trap的話,關閉信號每可能會被ping捕獲。
捕捉到信號之後 執行trap定義的操做。
複製命令的腳本,在作小linux系統的時間可能要複製命令之類的,對應的庫固然也要複製啦:
默認複製到/mnt/sysroot
#!/bin/bash # Author: xingxing # Version: 0.1 # Email: # Description: Move software(include library) # soft is software name # outPath is base output dirctory # softPath is software dirctory # Target root(/) dir. Target desk / partition have to mount here. outPath=/mnt/sysroot PATH=/bin:/sbin:/usr/bin:/usr/sbin createDir () { dirname=`dirname $1` if ! [ -d "$outPath$dirname" ];then mkdir -p $outPath$dirname fi } moveSoft () { createDir $softPath cp $softPath $outPath$softPath } moveLib () { ldd $softPath | while read linkData;do libPath=`echo $linkData | grep -o "/.*[[:space:]]"` [ -z "$libPath" ] && continue createDir $libPath cp $libPath $outPath$libPath done } while true;do read -p "Enter software name(quit): " soft if [ -z "$soft" ];then continue elif [ "$soft" == "quit" ];then break elif ! which $soft &> /dev/null;then if type $soft &> /dev/null;then echo -e "\033[35m$soft is build software\033[0m" else echo -e "\033[35m$$soft is not exist\033[0m" fi continue fi softPath=`\which $soft` if [ -e "$outPath$softPath" ];then echo -e "\033[35m$soft is already exist\033[0m" continue fi moveSoft retu1=$? moveLib retu2=$? [ $retu1 -eq 0 -a $retu2 -eq 0 ] && \ echo -e "\033[33m$soft move was success\033[0m" || \ echo -e "\033[35m$soft move was failed\033[0m" done
結果
就這樣吧。本身如今也不熟練,想起來什麼再來補充吧。但願能幫助到朋友們。
其它:
多進程:把執行放入後臺。
#!/bin/bash i=1 while [ $i -lt 10 ];do echo "$i"& ((i++)) done wait echo "dkfjdlkf"
結果:
多的語句能夠用大括號:
#!/bin/bash i=1 while [ $i -lt 10 ];do { echo "$i" echo "$i" }& ((i++)) done wait echo "dkfjdlkf"
wait 等待前面的後臺任務所有完成之後才往下執行。 否則結果會是這樣:
由於都在後臺,因此不會順序輸出,甚至腳本都退出了,後臺的程序還在輸出。
上面敲回車後進入正常的終端。