bash编程总结
本文為轉載文章,原出處為:http://blog.csdn.net/marcky/article/details/7549513
????bash應該是目前Linux上最流行的shell腳本解釋程序了(還有個shell叫dash,我太討厭這個東東了。),只要你在linux上工作,并且希望自己能夠工作得更愉悅,那么你應該熟悉最基本的bash編程,因為它將給你的工作帶來足夠的幸福感。本文將總結一些我自己平時使用的bash基本編程知識,和大家分享,也便于自己查詢。
變量
1、bash的變量名是區分大小寫的,并且變量名首字符不能是數字。看的各種代碼也不少了,說實話,我還真沒見到誰的代碼用數字開頭的變量名,我認為即使語言允許,這樣做的人也很少,除非你真的很特別。
2、變量定義與賦值
aaa=123
????這里需要注意定義變量時等號前后都不能有空格,必須緊靠著寫。雖然等號后面有空格的情況,語法可能不會出錯,但結果絕對是錯誤的。
3、變量拼接
bbb=${aaa}123
????很多時候,我們可能需要用一些變量、常量字符串等來拼接出一個新的變量,這時需要注意用來拼接的變量可能需要加上{},否則可能會出現變量識別錯誤從而找不到變量的情況。這種情況,我傾向于所有變量一股腦的全加上{}。
4、local和export
????變量定義時還有兩個常用的關鍵字——local和export。export在下文再說,定義局部變量的local,我卻基本不用,等我使用的時候再來補上總結。
???變量的定義也就那么回事,不去咬文嚼字的吭細節的話,懂這么一點點就夠用了。如果,你的目標是成為shell高手,那么就需要專業級的學習,linux系統中有不少龐大的shell可以學習。
條件判斷
???if條件表達中長涉及到的比較有字符串、整數和文件屬性比較等。
if語句格式有:
if?[?expr?]?;?then
??????do?something
fi
if?[?expr?]?;?then
??????do?something
else
??????do?something
fi
if?[?expr?]?;?then
?????do?something
elif?[?expr?]?;?then
?????do?something
else
?????so?something
fi
????if語句和其他語言(c,java)相比,是行不同但神似。then關鍵可以另起一行,那樣條件表達式后的分號就可以省略了。這里最需要注意的是?"?[?"?和?"?]?"前后至少需要一個空格來分割。
1、整數比較
???整數大小比較涉及的操作符有——?-lt、-le、-eq、-gt、-ge和?-ne。
例子:
a=1
b=2
if?[?$a?-lt?$b?]?;?then
????echo?"a?<?b"
else
????echo?"a?>=?b"
fi
if?[?$a?-ne?3?]?;?then
????echo?"a?!=?3"
else
????echo?"a?==?3"
fi
if?[?1?-gt?3?]?;?then
????echo?"1?>?3"
else
????echo?"1?<=?3"
fi
????使用整數大小比較的6個操作符時,涉及到的兩個操作數將會作為數值來處理,而不是字符串,即使你使用雙引號將比較對象給引起來,也是如此。
2、字符串比較
???字符串比較可能用到的操作符有——?=、!=、>、<、-n和-z。
例子:
s1=aaa
s2=bbb
if?[?$s1?\<?$s2?]?;?then
????echo?"$s1?<?$s2"
else
????echo?"$s1?>=?$s2"
fi
if?[[?$s1?<?$s2?]]?;?then
????echo?"$s1?<?$s2"
else
????echo?"$s1?>=?$s2"
fi
if?[?$s1?=?$s2?]?;?then
????echo?"$s1?==?$s2"
else
????echo?"$s1?!=?$s2"
fi
if?[?-n?$s1?]?;?then
????echo?$s1
fi
????用于字符串大小比較的>和<兩個操作符比較特別,在[?]中書寫時需要轉義,否則可以使用[[?]]來替代[?]。不轉義,>和<會被解釋為IO重定向操作。其次,if語句體不能為空,必須至少做一件事情。
???操作符-n?用于判斷字符串不為空,即長度不為0。-z判斷字符串為空,即長度為0。
3、文件屬性判斷
???文件屬性判斷涉及到的操作符比較多,如下:
-e、-a:文件存在
-d:是目錄
-f:是文件
-r:可讀
-w:可寫
-x:可執行
f1?-nt?f2:f1比f2新
f1?-ot?f2:f1比f2老
???這些操作符的使用方法和字符串操作符基本一致,在這幾個操作符中我最常用的還是-e和-f,其他用得較少。
???其實,除了這三類常用的判斷以外,還應該有執行命令結果的判斷也比較常用,本文將不予總結了。
for循環
???常用的for循環有兩種主要形式
形式一
for?e?in?[list]
do
????do?something
done
????這種格式的for常用來遍歷一個list集合中的所有元素,并加以處理。比如:
1、遍歷一個目錄中的所有文件
for?file?in?`ls?dir`
do
?????echo?$file
done
2、遍歷一個給定的集合
list="a?b?c?d?e?f?g"
for?e?in?$list
do
????echo?$e
done
for?in格式在遍歷集合時,其實是根據空白字符來分隔字符串,取得每個元素的,上例中的`ls?dir`和$list得到的都是一個帶空白字符的字符串。
形式二
for?((i?=?0;?i?<?n;?i++))
do
????do?something
done
這一種for的形式和C語言基本一致,只是需要雙括號罷了,它更擅長做確定次數的循環計算。比如:
for?((i?=?0;?i?<?10;?i++))
do
????echo?$i
done
for?((?;?;?))
do
????echo?"aaaa"
done
????沒有計數器的for循環就是一個死循環的實現,這和C語言的寫法也是一致的,真有親切感。
???掌握了這點for循環語句,我們就可以做很多的事情了,基本足夠我們玩了。
while循環
???這鳥蛋,我平時基本不用,一切需要循環的地方都用for。在這里列舉while循環的原因,就是想借它噴一下各種語言里的各種while,?do?while,還有什么until等。一個循環非得搞出各種不同的表達方式不可,這一點還是Golang做得最到位。語法糖太多,總有一天會膩死你。哈哈。
case語句
???case語句相當于絕大多數語言里的switch語句。這玩意除了具備if-elif的功能外,還支持通配符,這個相當有用。我們直接看例子。
例子:
url=www.tmall.com
case?$url?in
????www.taobao.com)??echo?1;;
????*.taobao.com)?????????echo?2;;
????*.tmall.com)?????????????echo?3;;
????www.tmall.com)??????echo?4;;
????*)???????????????????????????????echo?5;;
esac
????上例中條件分支不光有常量字符串,還有含通配符的字符串,這一點用來進行模式匹配非常便利。其次,需要注意case語言在匹配的過程中是從第一個開始逐一匹配,所有上例的輸出結果是3,而不是精確匹配的4。我認為這算一個小小的遺憾,要是支持精確匹配優先就更好玩了。
???其次,需要注意的是每個條件分支體的結束必須用雙分號。
???好了,case語言比較簡單,但很實用。
函數
函數定義
function?hello()
{
??????echo?"hello?world"
}
函數定義實用關鍵字function,函數名后面的括號可有可無。
函數調用
???無參數的函數調用只需要給出函數名就ok了,上面定義的函數直接用hello調用即可。有參數的函數,只需要將參數依次在函數名后面給出即可,如:func_name?arg1?arg2。
函數參數
???函數調用的時候可以給函數傳遞參數,那么函數體中又如何獲取這些參數呢??函數參數的獲取和腳本程序參數的獲取一致,都是通過$1、$2等來取得。比如:
function?add()
{
????n=$1
????m=$2
????echo?$((n?+?m))
}
調用add?1?2,將輸出3。
在函數中,可以通過$#的值來判斷函數調用的時候,傳遞了幾個參數。
bash函數里很少使用return這種方式來返回值。不過可以這樣調用函數來獲取計算結果,比如:
res=`add?1?2`
變量res的值就是3了。注意上面不是單引號,而是數字1旁邊的字符。
字符串處理
???字符串處理差不多是整個計算機世界里做得最頻繁的一件事情了。玩C語言的人很多事情都是在編寫字符串處理程序。玩java的人大多數時候雖然不用自己去編寫字符串處理程序,但也基本總是在調用字符串處理方法。linux其實有著非常強大的工具讓我們去做字符串處理,不熟悉之前,每個工具貌似長得都非常復雜的樣子。這里簡單的總結一下自己使用過的字符串相關的東東。
1、求子串
str=abcdefg
echo?${str:2:3}?將得到bcd,表達式中的2代表偏移量,3代表長度。
2、求字符串長度
str=123456
echo?${#str}
3、字符串替換
${變量/pattern/xx}???將變量中的第一個匹配替換為xx。
${變量//pattern/xx}?將變量中的所有匹配替換為xx。
str=aaabbbccc
echo?${str/aaa/xxx}
echo?${str/a/x}
????有關字符串的處理,有著很多的工具,比如:你可以使用cut,?awk等程序去拆分一個字符串等。
整數運算
???整數運算一般使用$((?expr?))來進行。比如:
echo?$((1?+?2))
a=1
echo?$((a?+?2))
echo?$((a++))
echo?$((++a))
b=2
echo?$(((a?+?b)?/?2))
????可以看到只需要將計算表達式塞到$(())中就可以了,參與計算的變量并不需要$符號。整數運算涉及到的運算符有:++?、--、+、-、*、/、%、+=、**(冪運算);?還有不常用的:<<、?>>、?^、?&、?!、?|、?~,這些在C語言中倒挺常用的,shell用來做這些位運算的需求也太2b了。?除了這些運算還支持邏輯:<、>、<=、>=、==、!=、&&、||。
???$((?expr?))表達式只能計算整數,不能用于小數的計算。如果,要做小數的計算,可以使用linux上的命令行計算器bc來完成。
例子:
echo?"1.5?+?1"?|?bc
代碼生成
???程序員應該是”懶惰“的,”懶惰“的程序員可以創造出更多的自動化工具。我喜歡用bash來輔助我完成一些代碼的自動生成,當然不是所有的代碼都可以自動生成,如果那樣也就不需要程序員了。我認為好的程序一定是可擴展的,最好的可擴展程序是”完成程序框架等開發后的功能需求開發階段不再需要過分的寫代碼,只需要填代碼“,能夠填的代碼,一定是可以自動生成的,這樣的程序開發就將形成一個良性的循環。最近我非常熱衷于重構我的代碼,盡量讓我的代碼可以用bash腳本來自動生成,這將極大的提高我的開發效率。
???如何自動生成代碼,生成什么樣的代碼,是根據自己的實際情況來完成的,這里沒法介紹具體的代碼生成,只能介紹bash用來完成代碼生成的工具——cat。使用過linux的人都知道cat,這里就不詳細說明了,只展示一段簡單的bash腳本輸出hello?world程序。
#!/bin/sh
cat?<<??END??>?hello.c
#include?<stdio.h>
int?main(void)
{
????printf("hello?world\n");
????return?0;
}
END
????在cat到END之間,你可以像在編輯器里一樣編寫自己的代碼,這里寫成什么樣子,輸出到源文件里就是什么樣子,包括縮進等格式。當然,真正有意義的代碼生成不會像這里的hello?world這么簡單,你必須得根據自己的實際情況去”拼湊“出自動化的代碼。這里僅僅是展示使用cat來生成一個源碼文件而已。要想自動生成真正有意義的,能提高自己開發效率的代碼,必須得學習絕大多數的bash編程知識。
source(.)與export
????使用linux的人都知道,我們敲打命令的終端其實就是運行在一個shell里的,這個shell里定義了很多的環境變量,比如:PATH,HOME等。然后,我在終端運行一個bash腳本程序后,這個bash腳本其實是運行在終端所在的shell?fork出來的一個子shell進程里。那么這個子shell為什么也能夠取得PATH,HOME這樣的環境變量呢?這就是因為這些變量在其父shell里被聲明為了export。export指令可以讓變量傳遞到子shell里,這在一個bash程序調用另外一個bash程序時,共享變量是非常的有用的。
????export可以讓變量傳遞到子shell里,但卻不能讓子shell的變量傳遞到父shell里,寫到這里,我覺得我這個轉折句說的很屁話。不過,bash中可以使用source命令來完成類似的功能。比如:我們可以在終端里用source來運行一個bash程序,那么這個程序中定義的變量,在程序結束后,依然存在。這是因為,通過source來執行一個腳本,并不會去fork出一個子shell來執行,而是直接在當前的shell里執行腳本中的所有語句。說到這里,我想你也會和我一樣認為,source就相當于C語言中的include指令。
???當一個腳本過大時,我熱衷于將它模塊化,然后將一些模塊獨立出來作為一個單獨的文件,再在主文件中通過source指令來執行之。如果,你和我一樣的懶,懶得連source都不想寫,那你完全可以使用點號(.)來替代source。
逐行讀文件
???當你在做文本處理的時候,可能需要將一個文件的內容一行一行的依次讀出,然后加以處理,bash做這個事情是非常的方便的。
例子:
while?read?line
do
????echo?$line
done?<?txt.log
????例子展示的是從文件txt.log中逐行的讀出來賦值給變量line。如果,你的文件內容是結構化的,比如:每行都是兩列,你想單獨的處理每一列的內容,那么bash提供了更友好的方式去逐行讀文件。
例子:
while?read?c1?c2
do
????echo?$c1?:?$c2
done
????這樣read文件,第一列就賦值給了c1,第二列就賦值給了c2。這有點ruby,python,go等優秀語言支持的多變量賦值的味道。
格式化輸出
???bash中最常用的輸出莫過于echo了,可是echo不能格式化的輸出,在自動生成代碼這樣的情況下,我想你有可能需要格式化輸出,讓代碼更加的整齊美觀。C語言有函數printf支持格式化輸出,linux也有printf工具,讓你如C語言一般隨心所欲的輸出。
printf的大致用法舉例:
printf?"my?name?is?%s,?I?am?%d?years?old.\n"??skoo?25
我想linux?printf的使用和C語言幾乎沒太大的區別,詳細的用法可以man?printf去。
附:sed和awk
????我想sed和awk,絕對是兩大神器,不過真正能駕馭它們的人并不多。我也只是略知皮毛,因此就不在這里廢話了。學習任何一門編程語言,到體現生產力的時候,都是拼庫。bash沒有庫函數的說法,但卻有著豐富的linux命令,要寫出真正強大有意義的bash程序,不得不學習一些linux命令,只有掌握了足夠的linux命令,才能駕輕就熟。就像大多數java程序員一樣,沒有豐富的庫,只掌握語法的他們也許什么有意義的事情都干不了。C/C++程序員也不例外,雖然他們熱衷于用僅有的一點語法去重造輪子。
?
轉載于:https://blog.51cto.com/vincentding/1343850
總結
- 上一篇: IPSEC ××× 互通性测试结果(二)
- 下一篇: 精神紧张、疲劳出现手抖时怎么了?