shell腳本編程 linux shell編程實例

您好,歡迎來到“C語言中文網(wǎng)-->Shell專題”,您將享受到免費的Shell教程和學習資料!

《Linux命令行與Shell腳本編程大全》第2版 PDF中文版

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)試變得簡單易行。

關(guān)于 shell 腳本編程的10 個最佳實踐

2、考慮問題要全面

現(xiàn)在我們來看個問題。你想到了關(guān)于某個問題的解決方案,但這個解決方案只適用于處理小型文件??墒钱斕幚肀容^大的文件時,你該怎么辦?舉個例子,我們想要得到一個文件的第一行內(nèi)容:

Shell

1
sed -n '1p' file

這條語句當然會給出你想要的第一行內(nèi)容??墒侨绻幚淼奈募习偃f條記錄呢?盡管上面的那條sed命令可以輸出文件的第一行內(nèi)容,但是想要處理大型文件一定會帶來性能上的問題。

解決辦法:

Shell

1
sed -n '1p;1q' file

這條命令將只輸出第一行,同時退出程序。

3、經(jīng)常嘗試不同的方法

你在寫腳本時碰到一個問題,然后你找到了一種獨特的解決方法。下一次你偶然又碰到類似的問題,這時,不要再用以前你用過的方法來解決。試試另外一種方法吧。如果某一天再次遇到這種情況,再試試其它方法。

例如:

Shell

1
2
3
4
if [ $? -eq 0 ]
then
?? echo "Success"
fi

另一種方法:

Shell

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命令會讓你的代碼看起來很丑陋,而且還會帶來性能上的問題。

例如:

Shell

1
$ cat /etc/passwd | grep guru

正確的方法應該是:

Shell

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ū)。

更多
  • 該日志由 于2017年12月13日發(fā)表在 未分類 分類下, 你可以發(fā)表評論,并在保留原文地址 及作者的情況下引用到你的網(wǎng)站或博客。
  • 本文鏈接: shell腳本編程 linux shell編程實例 | 幫助信息-動天數(shù)據(jù)
  • 文章標簽:
  • 版權(quán)所有: 幫助信息-動天數(shù)據(jù)-轉(zhuǎn)載請標明出處
  • 【上一篇】 【下一篇】

    0 Comments.

    發(fā)表評論