shell腳本編程 linux shell編程實例
您好,歡迎來到“C語言中文網(wǎng)-->Shell專題”,您將享受到免費的Shell教程和學習資料!
Shell 誕生于 Unix,是與 Unix/Linux 交互的工具,單獨地學習 Shell 是沒有意義的,請先參考Unix/Linux入門教程,了解 Unix/Lunix 基礎(chǔ)。
近幾年來,Shell一直被忽略,是一個不受重視的腳本語言。Shell雖然是Unix的第一個腳本語言,但它是相當優(yōu)秀的。它結(jié)合了延展性與效率,持續(xù)保有獨具的特色,并不斷的被改良,使它多年來能與那些花招很多的腳本語言保持抗衡。
Shell需要依賴其他程序才能完成大部分的工作,這或許是它的缺陷,但它不容置疑的長處是:簡潔的腳本語言標記方式,而且比C語言編寫的程序執(zhí)行更快、更有效率。
C語言中文網(wǎng)以通俗易懂的語言向您講解Shell編程,讓您在最短的時間內(nèi)快速掌握Shell,編寫出實用的程序和代碼。
為了讓您盡快體驗最新技術(shù),我們的教程將不斷更新,保持與時俱進,請及時關(guān)注。您的支持是我們前進的動力!
說明:C語言中文網(wǎng)的文章由團隊成員翻譯、編輯和整理,難免有錯誤和紕漏,請在文章底部留言向我們指出,謝謝。
Shell腳本編程的常識
現(xiàn)在我們使用的操作系統(tǒng)(Windows、Mac OS、Android、iOS 等)都是帶圖形界面的,簡單直觀,容易上手,對專業(yè)用戶(程序員、網(wǎng)管等)和普通用戶(家庭主婦、老年人等)都非常適用;計算機的普及離不開圖形界面。
然而在計算機的早期并沒有圖形界面,我們只能通過一個一個地命令來控制計算機,這些命令有成百上千之多,且不說記住這些命令非常困難,每天面對沒有任何色彩的“黑屏”本身就是一件枯燥的事情;這個時候的計算機還遠遠談不上炫酷和普及,只有專業(yè)人員才能使用。
猛擊《帶你逛西雅圖活電腦博物館》可以欣賞更多早期的計算機。
對于圖形界面,用戶點擊某個圖標就能啟動某個程序;對于命令行,用戶輸入某個程序的名字(可以看做一個命令)就能啟動某個程序。這兩者的基本過程都是類似的,都需要查找程序在硬盤上的安裝位置,然后將它們加載到內(nèi)存運行。
關(guān)于程序的運行原理,請猛擊《載入內(nèi)存,讓程序運行起來》。
換句話說,圖形界面和命令行要達到的目的是一樣的,都是讓用戶控制計算機。
然而,真正能夠控制計算機硬件(CPU、內(nèi)存、顯示器等)的只有操作系統(tǒng)內(nèi)核(Kernel),圖形界面和命令行只是架設(shè)在用戶和內(nèi)核之間的一座橋梁。
由于安全、復雜、繁瑣等原因,用戶不能直接接觸內(nèi)核(也沒有必要),需要另外再開發(fā)一個程序,讓用戶直接使用這個程序;該程序的作用就是接收用戶的操作(點擊圖標、輸入命令),并進行簡單的處理,然后再傳遞給內(nèi)核。如此一來,用戶和內(nèi)核之間就多了一層“代理”,這層“代理”既簡化了用戶的操作,也保護了內(nèi)核。
用戶界面和命令行就是這個另外開發(fā)的程序,就是這層“代理”。在Linux下,這個命令行程序叫做 Shell。
Shell 除了能解釋用戶輸入的命令,將它傳遞給內(nèi)核,還可以:
- 調(diào)用其他程序,給其他程序傳遞數(shù)據(jù)或參數(shù),并獲取程序的處理結(jié)果;
- 在多個程序之間傳遞數(shù)據(jù),把一個程序的輸出作為另一個程序的輸入;
- Shell 本身也可以被其他程序調(diào)用。
由此可見,Shell 是將內(nèi)核、程序和用戶連接了起來。
Shell 本身支持的命令并不多,但是它可以調(diào)用其他的程序,每個程序就是一個命令,這使得 Shell 命令的數(shù)量可以無限擴展,其結(jié)果就是 Shell 的功能非常強大,完全能夠勝任 Linux 的日常管理工作,如文本或字符串檢索、文件的查找或創(chuàng)建、大規(guī)模軟件的自動部署、更改系統(tǒng)設(shè)置、監(jiān)控服務器性能、發(fā)送報警郵件、抓取網(wǎng)頁內(nèi)容、壓縮文件等。
Shell 并不是簡單的堆砌命令,我們還可以在 Shell 中編程,這和使用 C/C++、Java、Python 等常見的編程語言并沒有什么兩樣。
Shell 雖然沒有 C/C++、Java、Python 等強大,但也支持了基本的編程元素,例如:
- if...else 選擇結(jié)構(gòu),switch...case 開關(guān)語句,for、while、until 循環(huán);
- 變量、數(shù)組、字符串、注釋、加減乘除、邏輯運算等概念;
- 函數(shù),包括用戶自定義的函數(shù)和內(nèi)置函數(shù)(例如 printf、export、eval 等)。
站在這個角度講,Shell 也是一種編程語言,它的編譯器(解釋器)是 Shell 這個程序。我們平時所說的 Shell,有時候是指連接用戶和內(nèi)核的這個程序,有時候又是指 Shell 編程。
Shell 主要用來開發(fā)一些實用的、自動化的小工具,而不是用來開發(fā)具有復雜業(yè)務邏輯的中大型軟件,例如檢測計算機的硬件參數(shù)、一鍵搭建Web開發(fā)環(huán)境、日志分析等,Shell 都非常合適。
使用 Shell 的熟練程度反映了用戶對 Linux 的掌握程度,運維工程師、網(wǎng)絡(luò)管理員、程序員都應該學習 Shell。
尤其是 Linux 運維工程師,Shell 更是必不可少的,是必須掌握的技能,它使得我們能夠自動化地管理服務器集群,否則你就得一個一個地登錄所有的服務器,對每一臺服務器都進行相同的設(shè)置,而這些服務器可能有成百上千之多,會浪費大量的時間在重復性的工作上。
Shell 是一種腳本語言
任何代碼最終都要被“翻譯”成二進制的形式才能在計算機中執(zhí)行。
有的編程語言,如 C/C++、Pascal、Go語言、匯編等,必須在程序運行之前將所有代碼都翻譯成二進制形式,也就是生成可執(zhí)行文件,用戶拿到的是最終生成的可執(zhí)行文件,看不到源碼。
這個過程叫做編譯(Compile),這樣的編程語言叫做編譯型語言,完成編譯過程的軟件叫做編譯器(Compiler)。
而有的編程語言,如 Shell、JavaScript、Python、PHP等,需要一邊執(zhí)行一邊翻譯,不會生成任何可執(zhí)行文件,用戶必須拿到源碼才能運行程序。程序運行后會即時翻譯,翻譯完一部分執(zhí)行一部分,不用等到所有代碼都翻譯完。
這個過程叫做解釋,這樣的編程語言叫做解釋型語言或者腳本語言(Script),完成解釋過程的軟件叫做解釋器。
編譯型語言的優(yōu)點是執(zhí)行速度快、對硬件要求低、保密性好,適合開發(fā)操作系統(tǒng)、大型應用程序、數(shù)據(jù)庫等。
腳本語言的優(yōu)點是使用靈活、部署容易、跨平臺性好,非常適合Web開發(fā)以及小工具的制作。
Shell 就是一種腳本語言,我們編寫完源碼后不用編譯,直接運行源碼即可。
(這些往往是經(jīng)常用到,但是各種網(wǎng)絡(luò)上的材料都語焉不詳?shù)臇|西,個人認為比較有用)
七種文件類型
d??????????? 目錄?????????????????????????????????????????????????????? l???????????? 符號鏈接
s???????????? 套接字文件?????????????????????????????????????????? b??????????? 塊設(shè)備文件
c??????????? 字符設(shè)備文件?????????????????????????????????????? p??????????? 命名管道文件
-???????????? 普通文件
正則表達式
從一個文件或命令輸出中抽取或過濾文本時??墒褂谜齽t表達式(RE),正則表達式是一些特殊或不很特殊的字符串模式的集合。
基本的元字符集:
^?????????????????? 只匹配行首。
$?????????????????? 只匹配行尾。
*?????????????????? 一個單字符后緊跟*,匹配0個或多個此單字符。
[]?????????????????? 匹配[]內(nèi)字符,可以是一個單字符,也可以是字符序列??梢允?/p>
用-來表示[]內(nèi)范圍,如[1-5]等價于[1,2,3,4,5]。
\??????????????????? 屏蔽一個元字符的特殊含義,如\$表示字符$,而不表示匹配行
尾。
.?? ????????????? 匹配任意單字符。
pattern\{n\}?? 匹配pattern出現(xiàn)的次數(shù)n
pattern\{n,\}m匹配pattern出現(xiàn)的次數(shù),但表示次數(shù)最少為n
pattern\{n,m\} 匹配pattern出現(xiàn)的次數(shù)在n與m之間(n,m為0-255)
幾個常見的例子:
顯示可執(zhí)行的文件:ls –l | grep …x...x..x
只顯示文件夾:ls –l | grep? ^d
匹配所有的空行:^$
匹配所有的單詞:[A-Z a-z]*
匹配任一非字母型字符:[^A-Z a-z]
包含八個字符的行:^……..$(8個.)
字符類描述
以下是可用字符類的相當完整的列表:
[:alnum:] 字母數(shù)字 [a-z A-Z 0-9]
[:alpha:] 字母 [a-z A-Z]
[:blank:] 空格或制表鍵
[:cntrl:] 任何控制字符
[:digit:] 數(shù)字 [0-9]
[:graph:] 任何可視字符(無空格)
[:lower:] 小寫 [a-z]
[:print:] 非控制字符
[:punct:] 標點字符
[:space:] 空格
[:upper:] 大寫 [A-Z]
[:xdigit:] 十六進制數(shù)字 [0-9 a-f A-F]
盡可能使用字符類是很有利的,因為它們可以更好地適應非英語 locale(包括某些必需的重音字符等等).
shell的引號類型
shell共有四種引用類型:
“ ”????????? 雙引號
‘ ’?????????? 單引號
` ` ??????? 反引號
\??????????? 反斜線
l??????? “ ” 可引用除$、` 、\ 、外的任意字符或字符串,“ ”中的變量能夠正常顯示變量值。
l??????? ‘ ’與“ ”類似,不同在于shell會忽略任何的引用值。
例如: GIRL=‘girl’
echo “The ‘$GIRL’ did well”
則打印:The ‘girl’ did well
l??????? ` `用于設(shè)置系統(tǒng)命令的輸出到變量,shell會將` `中的內(nèi)容作為一個系統(tǒng)命令并執(zhí)行質(zhì)。
例如:echo `date` 則打印當前的系統(tǒng)時間。
l??????? \ 用來屏蔽特殊含義的字符:&? *? +? ^? $? `? “? |? ?
例如:expr 12 \* 12 將輸出144
變量設(shè)置時的不同模式:
valiable_name=value?????????? 設(shè)置實際值到 variable_name中
valiable_name+value?????????? 如果設(shè)置了variable_name,則重設(shè)其值
valiable_name:?value?????????? 如果未設(shè)置variable_name,則先顯示未定義用戶錯誤信息
valiable_name?value?????????? 如果未設(shè)置variable_name,則顯示系統(tǒng)錯誤信息
valiable_name:=value?? 如果未設(shè)置variable_name,則設(shè)置其值
valiable_name-value??????????? 同上,但取值并不設(shè)置到variable_name
條件測試
test命令用于測試字符串、文件狀態(tài)和數(shù)字,expr測試和執(zhí)行數(shù)值輸出。
Test格式:test condition 或 [ condition ](需要特別注意的是condition的兩邊都要有一個空格,否則會報錯),test命令返回0表示成功。
l??????? 下面將分別描述test的三種測試:
n??????? 文件狀態(tài)測試(常用的)
-d?????????? 測試是否文件夾
-f??????????? 測試是否一般文件
-L????????? 測試是否鏈接文件
-r?????????? 測試文件是否可讀
-w ??????? 測試文件是否可寫
-x?????????? 測試文件是否可執(zhí)行
-s?????????? 測試文件是否非空
n??????? 字符串測試
五種格式: test? “string”
test? string_operator? “string”
test? “string”? string_operator? “string”
[ string_operator? “string” ]
[ “string”? string_operator? “string” ]
其中string_operator可以為:?????? =???? 兩字符串相等
!=??? 兩字符串不等
-z ? 空串
-n ? 非空串
n??????? 數(shù)值測試
兩種格式: “number”? number_operator? “number”
[ “number”? number_operator? “number” ]
其中:number_operator 可以為:-eq? 、-ne、-gt、-lt、-ge
例如:? NUMBER=130
[ “990”? –le? “995”? –a? “NUMBER”? -gt? “133” ]
(其中-a表示前后結(jié)果相“與”)
l??????? expr命令一般用于整數(shù)值,但也可以用于字符串。
n??????? 格式:? expr srgument operator operator argument
例如:? expr 10 + 10
expr 10 ^ 2 (10的平方)
expr $value + 10
n??????? 增量計數(shù)――expr在循環(huán)中最基本的用法
例如:? LOOP=0
LOOP=`expr $LOOP + 1`
n??????? 模式匹配:通過指定的冒號選項計算字符串中的字符數(shù)
例如:? value=account.doc
expr $value : `\(.*\).doc`
輸出 account
每一個在UNIX/Linux上工作的程序員可能都擅長shell腳本編程。但大家解決問題的方式卻不盡相同,這要取決于對專業(yè)知識的掌握程度、使用命令的種類、看待問題的方式等等。對于那些處在shell腳本編程初級階段的程序員來說,遵循一些恰當?shù)淖龇梢詭椭愀?、更好的學習這些編程技巧。下面,我們就來討論這些能幫助你學習shell腳本編程的方法吧。
0、多動手
你想學習shell腳本編程,這很不錯。于是你拿了一本書開始學習。一些人會首先通讀整本教材后再上機練習。這種方法可能適用于一些人,但我卻不太看好它。我的建議是,僅僅學一些最基礎(chǔ)的能夠讓你開始編碼的知識就可以了。之后,動手寫一些簡單的程序吧。一旦你由于知識上的欠缺而不得不停止時,再回到書本上去讀你想要了解的那部分,然后繼續(xù)做你的項目。如此周而復始,不斷提高你的水平。這種邊學邊做的方法曾讓我受益良多。
1、善用命令提示符
有時候,我們寫的腳本中有一些錯誤。我們修改錯誤,運行腳本,但系統(tǒng)再次報錯。并且這個改錯報錯的過程可能會發(fā)生很多次。碰到這些情況,首先需要找到有問題的行或命令,這可以通過一些調(diào)試語句來輕松做到。一旦發(fā)現(xiàn)這條語句,嘗試在命令提示符下執(zhí)行相同的語句。如果它在命令提示符下開始正常運行,你就可以容易的推斷出它不能正常運行的原因了??赡苁怯捎谀承╁e誤輸入的命令,或者是某些環(huán)境變量不匹配,或者是從不同的地方引用了某個二進制文件等等。這種方法會讓調(diào)試變得簡單易行。
2、考慮問題要全面
現(xiàn)在我們來看個問題。你想到了關(guān)于某個問題的解決方案,但這個解決方案只適用于處理小型文件??墒钱斕幚肀容^大的文件時,你該怎么辦?舉個例子,我們想要得到一個文件的第一行內(nèi)容:
1
|
sed -n '1p' file
|
這條語句當然會給出你想要的第一行內(nèi)容??墒侨绻幚淼奈募习偃f條記錄呢?盡管上面的那條sed命令可以輸出文件的第一行內(nèi)容,但是想要處理大型文件一定會帶來性能上的問題。
解決辦法:
1
|
sed -n '1p;1q' file
|
這條命令將只輸出第一行,同時退出程序。
3、經(jīng)常嘗試不同的方法
你在寫腳本時碰到一個問題,然后你找到了一種獨特的解決方法。下一次你偶然又碰到類似的問題,這時,不要再用以前你用過的方法來解決。試試另外一種方法吧。如果某一天再次遇到這種情況,再試試其它方法。
例如:
1
2
3
4
|
if [ $? -eq 0 ]
then
?? echo "Success"
fi
|
另一種方法:
1
|
[ $? -eq 0 ] && echo "Success"
|
現(xiàn)在你可能會明白這個博客里會有那么多以“……的不同解決方法”為題的文章了吧。所有這些文章的目的都是用來幫助訂閱這個博客的開發(fā)者開闊視野,打開思路。
4、快速編碼
腳本可以節(jié)省我們的時間,提高生產(chǎn)力??墒?,難道我們花在寫腳本和測試上的時間還少嗎?我們想寫一個腳本,于是打開一個文件,寫下代碼,保存文件,之后運行腳本,系統(tǒng)報錯,我們再打開文件修改、保存、運行……在這個過程中會花費很多時間。在此前的一篇題為《如何快速寫shell腳本》的文章里,你可以學會如何編寫腳本和測試正在運行中的腳本,而不用再回顧命令提示符。這些方法可以加快編碼的速度。當我寫腳本的時候,我總是使用這些方法。而且我可以很肯定的說,它們幫我節(jié)約了不少時間。
5、經(jīng)常使用內(nèi)部命令
無論碰到哪種情況,請盡量考慮使用內(nèi)部命令而不是外部命令。在此前的一篇題為《內(nèi)部命令和外部命令》的文章里,我們可以看到二者間的差異。用內(nèi)部命令對你永遠都有好處。根據(jù)正在處理的輸入文件的大小,內(nèi)部命令可以在性能方面為你節(jié)省很多。雖然你并不總是有這樣選擇內(nèi)部命令抑或外部命令的機會,但在某些情況下,你一定能做出正確的選擇。
6、沒有必要使用cat命令
這是我們經(jīng)常在論壇里討論的話題之一。沒有必要使用cat命令指的是在有些時候,我們會發(fā)現(xiàn)根本沒有必要使用cat命令。有時候,使用了多余的cat命令會讓你的代碼看起來很丑陋,而且還會帶來性能上的問題。
例如:
1
|
$ cat /etc/passwd | grep guru
|
正確的方法應該是:
1
|
$ grep guru /etc/passwd
|
7、仔細閱讀錯誤信息
程序員常犯的一個錯誤是:當我們敲入的命令報錯后,我們中的大多數(shù)人只是對錯誤信息一瞥而過,而不會去認真的讀一讀。很多時候,錯誤信息里就包含了解決辦法。更重要的是,有時候我們修改了某個錯誤并再次運行后,系統(tǒng)依舊會報錯。然后我們再次修改,但系統(tǒng)再次報錯。這可能會持續(xù)很長時間。但實際上,舊的錯誤可能已經(jīng)被糾正,只是由于出現(xiàn)了其它一些新錯誤才導致系統(tǒng)再次報錯。而我們依舊在懷疑為什么修改好的代碼依然不能正常運行。因此,請你養(yǎng)成仔細閱讀錯誤信息的習慣。
8、盡量避免臃腫的命令
你正在嘗試去從一個大的文件中篩選某條信息。接下來你可能寫一大堆命令來實現(xiàn)這一功能??墒?,盡管你將得到正確的結(jié)果,你寫的命令卻不夠好,且晦澀難懂。因此,我們應該盡量避免這種情況發(fā)生。下面這個例子就是代碼優(yōu)化的好例子。
從程序員的角度來看, Shell本身是一種用C語言編寫的程序,從用戶的角度來看,Shell是用戶與Linux操作系統(tǒng)溝通的橋梁。用戶既可以輸入命令執(zhí)行,又可以利用 Shell腳本編程,完成更加復雜的操作。在Linux GUI日益完善的今天,在系統(tǒng)管理等領(lǐng)域,Shell編程仍然起著不可忽視的作用。深入地了解和熟練地掌握Shell編程,是每一個Linux用戶的必修 功課之一。
Linux的Shell種類眾多,常見的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shell(/usr/bin/ksh)、Shell for Root(/sbin/sh),等等。不同的Shell語言的語法有所不同,所以不能交換使用。每種Shell都有其特色之處,基本上,掌握其中任何一種 就足夠了。在本文中,我們關(guān)注的重點是Bash,也就是Bourne Again Shell,由于易用和免費,Bash在日常工作中被廣泛使用;同時,Bash也是大多數(shù)Linux系統(tǒng)默認的Shell。在一般情況下,人們并不區(qū)分 Bourne Shell和Bourne Again Shell,所以,在下面的文字中,我們可以看到#!/bin/sh,它同樣也可以改為#!/bin/bash。
利用vi等文本編輯器編寫Shell腳本的格式是固定的,如下:
1
2
3
|
#!/bin/sh #comments Your commands go here |
首行中的符號#!告訴系統(tǒng)其后路徑所指定的程序即是解釋此腳本文件的Shell程 序。如果首行沒有這句話,在執(zhí)行腳本文件的時候,將會出現(xiàn)錯誤。后續(xù)的部分就是主程序,Shell腳本像高級語言一樣,也有變量賦值,也有控制語句。除第 一行外,以#開頭的行就是注釋行,直到此行的結(jié)束。如果一行未完成,可以在行尾加上",這個符號表明下一行與此行會合并為同一行。
編輯完畢,將腳本存盤為filename.sh,文件名后綴sh表明這是一個Bash腳本文件。執(zhí)行腳本的時候,要先將腳本文件的屬性改為可執(zhí)行的:
1
|
chmod +x filename.sh |
執(zhí)行腳本的方法是:
1
|
. /filename .sh |
下面我們從經(jīng)典的“hello world”入手,看一看最簡單的Shell腳本的模樣。
1
2
3
4
|
#!/bin/sh #print hello world in the console window a = "hello world" echo $a |
Shell Script是一種弱類型語言,使用變量的時候無需首先聲明其類型。新的變量會在本地數(shù)據(jù)區(qū)分配內(nèi)存進行存儲,這個變量歸當前的Shell所有,任何子進 程都不能訪問本地變量。這些變量與環(huán)境變量不同,環(huán)境變量被存儲在另一內(nèi)存區(qū),叫做用戶環(huán)境區(qū),這塊內(nèi)存中的變量可以被子進程訪問。變量賦值的方式是:
1
|
variable_name = variable_value |
如果對一個已經(jīng)有值的變量賦值,新值將取代舊值。取值的時候要在變量名前加$,$variable_name可以在引號中使用,這一點和其他高級語言是明顯不同的。如果出現(xiàn)混淆的情況,可以使用花括號來區(qū)分,例如:
echo "Hi, $as"
就不會輸出“Hi, hello worlds”,而是輸出“Hi,”。這是因為Shell把$as當成一個變量,而$as未被賦值,其值為空。正確的方法是:
echo "Hi, ${a}s"
單引號中的變量不會進行變量替換操作。
關(guān)于變量,還需要知道幾個與其相關(guān)的Linux命令。
env用于顯示用戶環(huán)境區(qū)中的變量及其取值;set用于顯示本地數(shù)據(jù)區(qū)和用戶環(huán)境區(qū)中的變量及其取值;unset用于刪除指定變量當前的取值,該值將被指定為NULL;export命令用于將本地數(shù)據(jù)區(qū)中的變量轉(zhuǎn)移到用戶環(huán)境區(qū)。
0 Comments.