Linux Shell 编程学习总结
生活随笔
收集整理的這篇文章主要介紹了
Linux Shell 编程学习总结
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Shell 教程
Shell簡介:什么是Shell,Shell命令的兩種執(zhí)行方式
Shell本身是一個用C語言編寫的程序,它是用戶使用Unix/Linux的橋梁,用戶的大部分工作都是通過Shell完成的。Shell既是一種命令語言,又是一種程序設(shè)計語言。作為命令語言,它交互式地解釋和執(zhí)行用戶輸入的命令;作為程序設(shè)計語言,它定義了各種變量和參數(shù)
,并提供了許多在高級語言中才具有的控制結(jié)構(gòu),包括循環(huán)和分支。
它雖然不是Unix/Linux系統(tǒng)內(nèi)核的一部分,但它調(diào)用了系統(tǒng)核心的大部分功能來執(zhí)行程序、建立文件并以并行的方式協(xié)調(diào)各個程序的運行。因此,對于用戶來說,shell是最重要的實用程序,深入了解和熟練掌握shell的特性極其使用方法,是用好Unix/Linux系統(tǒng)的關(guān)鍵
。
可以說,shell使用的熟練程度反映了用戶對Unix/Linux使用的熟練程度。
注意:單獨地學(xué)習(xí) Shell 是沒有意義的,請先參考Unix/Linux入門教程,了解 Unix/Linux 基礎(chǔ)。
Shell有兩種執(zhí)行命令的方式:
交互式(Interactive):解釋執(zhí)行用戶的命令,用戶輸入一條命令,Shell就解釋執(zhí)行一條。
批處理(Batch):用戶事先寫一個Shell腳本(Script),其中有很多條命令,讓Shell一次把這些命令執(zhí)行完,而不必一條一條地敲命令。
Shell腳本和編程語言很相似,也有變量和流程控制語句,但Shell腳本是解釋執(zhí)行的,不需要編譯,Shell程序從腳本中一行一行讀取并執(zhí)行這些命令,相當(dāng)于一個用戶把腳本中的命令一行一行敲到Shell提示符下執(zhí)行。
Shell初學(xué)者請注意,在平常應(yīng)用中,建議不要用 root 帳號運行 Shell 。作為普通用戶,不管您有意還是無意,都無法破壞系統(tǒng);但如果是 root,那就不同了,只要敲幾個字母,就可能導(dǎo)致災(zāi)難性后果。
====
幾種常見的Shell
上面提到過,Shell是一種腳本語言,那么,就必須有解釋器來執(zhí)行這些腳本。
Unix/Linux上常見的Shell腳本解釋器有bash、sh、csh、ksh等,習(xí)慣上把它們稱作一種Shell。我們常說有多少種Shell,其實說的是Shell腳本解釋器。
bash
bash是Linux標(biāo)準(zhǔn)默認(rèn)的shell,本教程也基于bash講解。bash由Brian Fox和Chet Ramey共同完成,是BourneAgain Shell的縮寫,內(nèi)部命令一共有40個。
Linux使用它作為默認(rèn)的shell是因為它有諸如以下的特色:
可以使用類似DOS下面的doskey的功能,用方向鍵查閱和快速輸入并修改命令。
自動通過查找匹配的方式給出以某字符串開頭的命令。
包含了自身的幫助功能,你只要在提示符下面鍵入help就可以得到相關(guān)的幫助。
sh
sh 由Steve Bourne開發(fā),是Bourne Shell的縮寫,sh 是Unix 標(biāo)準(zhǔn)默認(rèn)的shell。
ash
ash shell 是由Kenneth Almquist編寫的,Linux中占用系統(tǒng)資源最少的一個小shell,它只包含24個內(nèi)部命令,因而使用起來很不方便。
csh
csh 是Linux比較大的內(nèi)核,它由以William Joy為代表的共計47位作者編成,共有52個內(nèi)部命令。該shell其實是指向/bin/tcsh這樣的一個shell,也就是說,csh其實就是tcsh。
ksh
ksh 是Korn shell的縮寫,由Eric Gisin編寫,共有42條內(nèi)部命令。該shell最大的優(yōu)點是幾乎和商業(yè)發(fā)行版的ksh完全兼容,這樣就可以在不用花錢購買商業(yè)版本的情況下嘗試商業(yè)版本的性能了。
注意:bash是 Bourne Again Shell 的縮寫,是linux標(biāo)準(zhǔn)的默認(rèn)shell ,它基于Bourne shell,吸收了C shell和Korn shell的一些特性。bash完全兼容sh,也就是說,用sh寫的腳本可以不加修改的在bash中執(zhí)行。
====
Shell腳本語言與編譯型語言的差異
大體上,可以將程序設(shè)計語言可以分為兩類:編譯型語言和解釋型語言
編譯型語言
很多傳統(tǒng)的程序設(shè)計語言,例如Fortran、Ada、Pascal、C、C++和Java,都是編譯型語言。這類語言需要預(yù)先將我們寫好的源代碼(source code)轉(zhuǎn)換成目標(biāo)代碼(object code),這個過程被稱作“編譯”。
運行程序時,直接讀取目標(biāo)代碼(object code)。由于編譯后的目標(biāo)代碼(object code)非常接近計算機(jī)底層,因此執(zhí)行效率很高,這是編譯型語言的優(yōu)點。
但是,由于編譯型語言多半運作于底層,所處理的是字節(jié)、整數(shù)、浮點數(shù)或是其他機(jī)器層級的對象,往往實現(xiàn)一個簡單的功能需要大量復(fù)雜的代碼。例如,在C++里,就很難進(jìn)行“將一個目錄里所有的文件復(fù)制到另一個目錄中”之類的簡單操作。
解釋型語言
解釋型語言也被稱作“腳本語言”。執(zhí)行這類程序時,解釋器(interpreter)需要讀取我們編寫的源代碼(source code),并將其轉(zhuǎn)換成目標(biāo)代碼(object code),再由計算機(jī)運行。因為每次執(zhí)行程序都多了編譯的過程,因此效率有所下降。
使用腳本編程語言的好處是,它們多半運行在比編譯型語言還高的層級,能夠輕易處理文件與目錄之類的對象;缺點是它們的效率通常不如編譯型語言。不過權(quán)衡之下,通常使用腳本編程還是值得的:花一個小時寫成的簡單腳本,同樣的功能用C或C++來編寫實現(xiàn),可能
需要兩天,而且一般來說,腳本執(zhí)行的速度已經(jīng)夠快了,快到足以讓人忽略它性能上的問題。腳本編程語言的例子有awk、Perl、Python、Ruby與Shell。
====
什么時候使用Shell
因為Shell似乎是各UNIX系統(tǒng)之間通用的功能,并且經(jīng)過了POSIX的標(biāo)準(zhǔn)化。因此,Shell腳本只要“用心寫”一次,即可應(yīng)用到很多系統(tǒng)上。因此,之所以要使用Shell腳本是基于:
簡單性:Shell是一個高級語言;通過它,你可以簡潔地表達(dá)復(fù)雜的操作。
可移植性:使用POSIX所定義的功能,可以做到腳本無須修改就可在不同的系統(tǒng)上執(zhí)行。
開發(fā)容易:可以在短時間內(nèi)完成一個功能強(qiáng)大又妤用的腳本。
但是,考慮到Shell腳本的命令限制和效率問題,下列情況一般不使用Shell:
資源密集型的任務(wù),尤其在需要考慮效率時(比如,排序,hash等等)。
需要處理大任務(wù)的數(shù)學(xué)操作,尤其是浮點運算,精確運算,或者復(fù)雜的算術(shù)運算(這種情況一般使用C++或FORTRAN 來處理)。
有跨平臺(操作系統(tǒng))移植需求(一般使用C 或Java)。
復(fù)雜的應(yīng)用,在必須使用結(jié)構(gòu)化編程的時候(需要變量的類型檢查,函數(shù)原型,等等)。
對于影響系統(tǒng)全局性的關(guān)鍵任務(wù)應(yīng)用。
對于安全有很高要求的任務(wù),比如你需要一個健壯的系統(tǒng)來防止入侵、破解、惡意破壞等等。
項目由連串的依賴的各個部分組成。
需要大規(guī)模的文件操作。
需要多維數(shù)組的支持。
需要數(shù)據(jù)結(jié)構(gòu)的支持,比如鏈表或數(shù)等數(shù)據(jù)結(jié)構(gòu)。
需要產(chǎn)生或操作圖形化界面 GUI。
需要直接操作系統(tǒng)硬件。
需要 I/O 或socket 接口。
需要使用庫或者遺留下來的老代碼的接口。
私人的、閉源的應(yīng)用(shell 腳本把代碼就放在文本文件中,全世界都能看到)。
如果你的應(yīng)用符合上邊的任意一條,那么就考慮一下更強(qiáng)大的語言吧——或許是Perl、Tcl、Python、Ruby——或者是更高層次的編譯語言比如C/C++,或者是Java。即使如此,你會發(fā)現(xiàn),使用shell來原型開發(fā)你的應(yīng)用,在開發(fā)步驟中也是非常有用的。
====
第一個Shell腳本
打開文本編輯器,新建一個文件,擴(kuò)展名為sh(sh代表shell),擴(kuò)展名并不影響腳本執(zhí)行,見名知意就好,如果你用php寫shell 腳本,擴(kuò)展名就用php好了。
輸入一些代碼:
#!/bin/bash
echo "Hello World !"
“#!” 是一個約定的標(biāo)記,它告訴系統(tǒng)這個腳本需要什么解釋器來執(zhí)行,即使用哪一種Shell。echo命令用于向窗口輸出文本。
運行Shell腳本有兩種方法。
作為可執(zhí)行程序
將上面的代碼保存為test.sh,并 cd 到相應(yīng)目錄:
chmod +x ./test.sh ?#使腳本具有執(zhí)行權(quán)限
./test.sh ?#執(zhí)行腳本
注意,一定要寫成./test.sh,而不是test.sh。運行其它二進(jìn)制的程序也一樣,直接寫test.sh,linux系統(tǒng)會去PATH里尋找有沒有叫test.sh的,而只有/bin, /sbin, /usr/bin,/usr/sbin等在PATH里,你的當(dāng)前目錄通常不在PATH里,所以寫成test.sh是會找不到命令的
,要用./test.sh告訴系統(tǒng)說,就在當(dāng)前目錄找。
通過這種方式運行bash腳本,第一行一定要寫對,好讓系統(tǒng)查找到正確的解釋器。
這里的"系統(tǒng)",其實就是shell這個應(yīng)用程序(想象一下Windows Explorer),但我故意寫成系統(tǒng),是方便理解,既然這個系統(tǒng)就是指shell,那么一個使用/bin/sh作為解釋器的腳本是不是可以省去第一行呢?是的。
作為解釋器參數(shù)
這種運行方式是,直接運行解釋器,其參數(shù)就是shell腳本的文件名,如:
/bin/sh test.sh
/bin/php test.php
這種方式運行的腳本,不需要在第一行指定解釋器信息,寫了也沒用。
再看一個例子。下面的腳本使用 read 命令從 stdin 獲取輸入并賦值給 PERSON 變量,最后在 stdout 上輸出:
#!/bin/bash
# Author : mozhiyan
# Copyright (c) http://see.xidian.edu.cn/cpp/linux/
# Script follows here:
echo "What is your name?"
read PERSON
echo "Hello, $PERSON"
運行腳本:
chmod +x ./test.sh
$./test.sh
What is your name?
mozhiyan
Hello, mozhiyan
$
====
Shell變量:Shell變量的定義、刪除變量、只讀變量、變量類型
Shell支持自定義變量。
定義變量
定義變量時,變量名不加美元符號($),如:
variableName="value"
注意,變量名和等號之間不能有空格,這可能和你熟悉的所有編程語言都不一樣。同時,變量名的命名須遵循如下規(guī)則:
首個字符必須為字母(a-z,A-Z)。
中間不能有空格,可以使用下劃線(_)。
不能使用標(biāo)點符號。
不能使用bash里的關(guān)鍵字(可用help命令查看保留關(guān)鍵字)。
變量定義舉例:
myUrl="http://see.xidian.edu.cn/cpp/linux/"
myNum=100
使用變量
使用一個定義過的變量,只要在變量名前面加美元符號($)即可,如:
your_name="mozhiyan"
echo $your_name
echo ${your_name}
變量名外面的花括號是可選的,加不加都行,加花括號是為了幫助解釋器識別變量的邊界,比如下面這種情況:
for skill in Ada Coffe Action Java?
do
? ? echo "I am good at ${skill}Script"
done
如果不給skill變量加花括號,寫成echo "I am good at $skillScript",解釋器就會把$skillScript當(dāng)成一個變量(其值為空),代碼執(zhí)行結(jié)果就不是我們期望的樣子了。
推薦給所有變量加上花括號,這是個好的編程習(xí)慣。
重新定義變量
已定義的變量,可以被重新定義,如:
myUrl="http://see.xidian.edu.cn/cpp/linux/"
echo ${myUrl}
myUrl="http://see.xidian.edu.cn/cpp/shell/"
echo ${myUrl}
這樣寫是合法的,但注意,第二次賦值的時候不能寫 $myUrl="http://see.xidian.edu.cn/cpp/shell/",使用變量的時候才加美元符($)。
只讀變量
使用 readonly 命令可以將變量定義為只讀變量,只讀變量的值不能被改變。
下面的例子嘗試更改只讀變量,結(jié)果報錯:
#!/bin/bash
myUrl="http://see.xidian.edu.cn/cpp/shell/"
readonly myUrl
myUrl="http://see.xidian.edu.cn/cpp/danpianji/"
運行腳本,結(jié)果如下:
/bin/sh: NAME: This variable is read only.
刪除變量
使用 unset 命令可以刪除變量。語法:
unset variable_name
變量被刪除后不能再次使用;unset 命令不能刪除只讀變量。
舉個例子:
#!/bin/sh
myUrl="http://see.xidian.edu.cn/cpp/u/xitong/"
unset myUrl
echo $myUrl
上面的腳本沒有任何輸出。
變量類型
運行shell時,會同時存在三種變量:
1) 局部變量
局部變量在腳本或命令中定義,僅在當(dāng)前shell實例中有效,其他shell啟動的程序不能訪問局部變量。
2) 環(huán)境變量
所有的程序,包括shell啟動的程序,都能訪問環(huán)境變量,有些程序需要環(huán)境變量來保證其正常運行。必要的時候shell腳本也可以定義環(huán)境變量。
3) shell變量
shell變量是由shell程序設(shè)置的特殊變量。shell變量中有一部分是環(huán)境變量,有一部分是局部變量,這些變量保證了shell的正常運行
====
Shell特殊變量:Shell $0, $#, $*, $@, $?, $$和命令行參數(shù)
前面已經(jīng)講到,變量名只能包含數(shù)字、字母和下劃線,因為某些包含其他字符的變量有特殊含義,這樣的變量被稱為特殊變量。
例如,$ 表示當(dāng)前Shell進(jìn)程的ID,即pid,看下面的代碼:
$echo $$
運行結(jié)果
29949
特殊變量列表
變量 含義
$0 當(dāng)前腳本的文件名
$n 傳遞給腳本或函數(shù)的參數(shù)。n 是一個數(shù)字,表示第幾個參數(shù)。例如,第一個參數(shù)是$1,第二個參數(shù)是$2。
$# 傳遞給腳本或函數(shù)的參數(shù)個數(shù)。
$* 傳遞給腳本或函數(shù)的所有參數(shù)。
$@ 傳遞給腳本或函數(shù)的所有參數(shù)。被雙引號(" ")包含時,與 $* 稍有不同,下面將會講到。
$? 上個命令的退出狀態(tài),或函數(shù)的返回值。
$$ 當(dāng)前Shell進(jìn)程ID。對于 Shell 腳本,就是這些腳本所在的進(jìn)程ID。
命令行參數(shù)
運行腳本時傳遞給腳本的參數(shù)稱為命令行參數(shù)。命令行參數(shù)用 $n 表示,例如,$1 表示第一個參數(shù),$2 表示第二個參數(shù),依次類推。
請看下面的腳本:
#!/bin/bash
echo "File Name: $0"
echo "First Parameter : $1"
echo "First Parameter : $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "Total Number of Parameters : $#"
運行結(jié)果:
$./test.sh Zara Ali
File Name : ./test.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2
$* 和 $@ 的區(qū)別
$* 和 $@ 都表示傳遞給函數(shù)或腳本的所有參數(shù),不被雙引號(" ")包含時,都以"$1" "$2" … "$n" 的形式輸出所有參數(shù)。
但是當(dāng)它們被雙引號(" ")包含時,"$*" 會將所有的參數(shù)作為一個整體,以"$1 $2 … $n"的形式輸出所有參數(shù);"$@" 會將各個參數(shù)分開,以"$1" "$2" … "$n" 的形式輸出所有參數(shù)。
下面的例子可以清楚的看到 $* 和 $@ 的區(qū)別:
#!/bin/bash
echo "\$*=" $*
echo "\"\$*\"=" "$*"
echo "\$@=" $@
echo "\"\$@\"=" "$@"
echo "print each param from \$*"
for var in $*
do
? ? echo "$var"
done
echo "print each param from \$@"
for var in $@
do
? ? echo "$var"
done
echo "print each param from \"\$*\""
for var in "$*"
do
? ? echo "$var"
done
echo "print each param from \"\$@\""
for var in "$@"
do
? ? echo "$var"
done
執(zhí)行 ./test.sh "a" "b" "c" "d",看到下面的結(jié)果:
$*= ?a b c d
"$*"= a b c d
$@= ?a b c d
"$@"= a b c d
print each param from $*
a
b
c
d
print each param from $@
a
b
c
d
print each param from "$*"
a b c d
print each param from "$@"
a
b
c
d
退出狀態(tài)
$? 可以獲取上一個命令的退出狀態(tài)。所謂退出狀態(tài),就是上一個命令執(zhí)行后的返回結(jié)果。
退出狀態(tài)是一個數(shù)字,一般情況下,大部分命令執(zhí)行成功會返回 0,失敗返回 1。
不過,也有一些命令返回其他值,表示不同類型的錯誤。
下面例子中,命令成功執(zhí)行:
$./test.sh Zara Ali
File Name : ./test.sh
First Parameter : Zara
Second Parameter : Ali
Quoted Values: Zara Ali
Quoted Values: Zara Ali
Total Number of Parameters : 2
$echo $?
0
$
$? 也可以表示函數(shù)的返回值,后續(xù)將會講解。
====
Shell替換:Shell變量替換,命令替換,轉(zhuǎn)義字符
如果表達(dá)式中包含特殊字符,Shell 將會進(jìn)行替換。例如,在雙引號中使用變量就是一種替換,轉(zhuǎn)義字符也是一種替換。
舉個例子:
#!/bin/bash
a=10
echo -e "Value of a is $a \n"
運行結(jié)果:
Value of a is 10
這里 -e 表示對轉(zhuǎn)義字符進(jìn)行替換。如果不使用 -e 選項,將會原樣輸出:
Value of a is 10\n
下面的轉(zhuǎn)義字符都可以用在 echo 中:
轉(zhuǎn)義字符 含義
\\ 反斜杠
\a 警報,響鈴
\b 退格(刪除鍵)
\f 換頁(FF),將當(dāng)前位置移到下頁開頭
\n 換行
\r 回車
\t 水平制表符(tab鍵)?
\v 垂直制表符 可以使用 echo 命令的 -E 選項禁止轉(zhuǎn)義,默認(rèn)也是不轉(zhuǎn)義的;使用 -n 選項可以禁止插入換行符。
命令替換
命令替換是指Shell可以先執(zhí)行命令,將輸出結(jié)果暫時保存,在適當(dāng)?shù)牡胤捷敵觥?
命令替換的語法:
`command`
注意是反引號,不是單引號,這個鍵位于 Esc 鍵下方。
下面的例子中,將命令執(zhí)行結(jié)果保存在變量中:
#!/bin/bash
DATE=`date`
echo "Date is $DATE"
USERS=`who | wc -l`
echo "Logged in user are $USERS"
UP=`date ; uptime`
echo "Uptime is $UP"
運行結(jié)果:
Date is Thu Jul ?2 03:59:57 MST 2009
Logged in user are 1
Uptime is Thu Jul ?2 03:59:57 MST 2009
03:59:57 up 20 days, 14:03, ?1 user, ?load avg: 0.13, 0.07, 0.15
變量替換
變量替換可以根據(jù)變量的狀態(tài)(是否為空、是否定義等)來改變它的值
可以使用的變量替換形式:
形式 說明
${var} 變量本來的值
${var:-word} 如果變量 var 為空或已被刪除(unset),那么返回 word,但不改變 var 的值。
${var:=word} 如果變量 var 為空或已被刪除(unset),那么返回 word,并將 var 的值設(shè)置為 word。
${var:?message} 如果變量 var 為空或已被刪除(unset),那么將消息 message 送到標(biāo)準(zhǔn)錯誤輸出,可以用來檢測變量 var 是否可以被正常賦值。
若此替換出現(xiàn)在Shell腳本中,那么腳本將停止運行。
${var:+word} 如果變量 var 被定義,那么返回 word,但不改變 var 的值。
請看下面的例子:
#!/bin/bash
echo ${var:-"Variable is not set"}
echo "1 - Value of var is ${var}"
echo ${var:="Variable is not set"}
echo "2 - Value of var is ${var}"
unset var
echo ${var:+"This is default value"}
echo "3 - Value of var is $var"
var="Prefix"
echo ${var:+"This is default value"}
echo "4 - Value of var is $var"
echo ${var:?"Print this message"}
echo "5 - Value of var is ${var}"
運行結(jié)果:
Variable is not set
1 - Value of var is
Variable is not set
2 - Value of var is Variable is not set
3 - Value of var is
This is default value
4 - Value of var is Prefix
Prefix
5 - Value of var is Prefix
====
Shell運算符:Shell算數(shù)運算符、關(guān)系運算符、布爾運算符、字符串運算符等
Bash 支持很多運算符,包括算數(shù)運算符、關(guān)系運算符、布爾運算符、字符串運算符和文件測試運算符。
原生bash不支持簡單的數(shù)學(xué)運算,但是可以通過其他命令來實現(xiàn),例如 awk 和 expr,expr 最常用。
expr 是一款表達(dá)式計算工具,使用它能完成表達(dá)式的求值操作。
例如,兩個數(shù)相加:
#!/bin/bash
val=`expr 2 + 2`
echo "Total value : $val"
運行腳本輸出:
Total value : 4
兩點注意:
表達(dá)式和運算符之間要有空格,例如 2+2 是不對的,必須寫成 2 + 2,這與我們熟悉的大多數(shù)編程語言不一樣。
完整的表達(dá)式要被 ` ` 包含,注意這個字符不是常用的單引號,在 Esc 鍵下邊。
算術(shù)運算符
先來看一個使用算術(shù)運算符的例子:
#!/bin/sh
a=10
b=20
val=`expr $a + $b`
echo "a + b : $val"
val=`expr $a - $b`
echo "a - b : $val"
val=`expr $a \* $b`
echo "a * b : $val"
val=`expr $b / $a`
echo "b / a : $val"
val=`expr $b % $a`
echo "b % a : $val"
if [ $a == $b ]
then
? ?echo "a is equal to b"
fi
if [ $a != $b ]
then
? ?echo "a is not equal to b"
fi
運行結(jié)果:
a + b : 30
a - b : -10
a * b : 200
b / a : 2
b % a : 0
a is not equal to b
注意:
乘號(*)前邊必須加反斜杠(\)才能實現(xiàn)乘法運算;
if...then...fi 是條件語句,后續(xù)將會講解。
算術(shù)運算符列表
運算符 說明 舉例
+ 加法 `expr $a + $b` 結(jié)果為 30。
- 減法 `expr $a - $b` 結(jié)果為 10。
* 乘法 `expr $a \* $b` 結(jié)果為 ?200。
/ 除法 `expr $b / $a` 結(jié)果為 2。
% 取余 `expr $b % $a` 結(jié)果為 0。
= 賦值 a=$b 將把變量 b 的值賦給 a。
== 相等。用于比較兩個數(shù)字,相同則返回 true。 [ $a == $b ] 返回 false。
!= 不相等。用于比較兩個數(shù)字,不相同則返回 true。 [ $a != $b ] 返回 true。
注意:條件表達(dá)式要放在方括號之間,并且要有空格,例如 [$a==$b] 是錯誤的,必須寫成 [ $a == $b ]。
關(guān)系運算符
關(guān)系運算符只支持?jǐn)?shù)字,不支持字符串,除非字符串的值是數(shù)字。
先來看一個關(guān)系運算符的例子:
#!/bin/sh
a=10
b=20
if [ $a -eq $b ]
then
? ?echo "$a -eq $b : a is equal to b"
else
? ?echo "$a -eq $b: a is not equal to b"
fi
if [ $a -ne $b ]
then
? ?echo "$a -ne $b: a is not equal to b"
else
? ?echo "$a -ne $b : a is equal to b"
fi
if [ $a -gt $b ]
then
? ?echo "$a -gt $b: a is greater than b"
else
? ?echo "$a -gt $b: a is not greater than b"
fi
if [ $a -lt $b ]
then
? ?echo "$a -lt $b: a is less than b"
else
? ?echo "$a -lt $b: a is not less than b"
fi
if [ $a -ge $b ]
then
? ?echo "$a -ge $b: a is greater or ?equal to b"
else
? ?echo "$a -ge $b: a is not greater or equal to b"
fi
if [ $a -le $b ]
then
? ?echo "$a -le $b: a is less or ?equal to b"
else
? ?echo "$a -le $b: a is not less or equal to b"
fi
運行結(jié)果:
10 -eq 20: a is not equal to b
10 -ne 20: a is not equal to b
10 -gt 20: a is not greater than b
10 -lt 20: a is less than b
10 -ge 20: a is not greater or equal to b
10 -le 20: a is less or ?equal to b
關(guān)系運算符列表
運算符 說明 舉例
-eq 檢測兩個數(shù)是否相等,相等返回 true。 [ $a -eq $b ] 返回 true。
-ne 檢測兩個數(shù)是否相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 檢測左邊的數(shù)是否大于右邊的,如果是,則返回 true。 [ $a -gt $b ] 返回 false。
-lt 檢測左邊的數(shù)是否小于右邊的,如果是,則返回 true。 [ $a -lt $b ] 返回 true。
-ge 檢測左邊的數(shù)是否大等于右邊的,如果是,則返回 true。 [ $a -ge $b ] 返回 false。
-le 檢測左邊的數(shù)是否小于等于右邊的,如果是,則返回 true。 [ $a -le $b ] 返回 true。
布爾運算符
先來看一個布爾運算符的例子:
#!/bin/sh
a=10
b=20
if [ $a != $b ]
then
? ?echo "$a != $b : a is not equal to b"
else
? ?echo "$a != $b: a is equal to b"
fi
if [ $a -lt 100 -a $b -gt 15 ]
then
? ?echo "$a -lt 100 -a $b -gt 15 : returns true"
else
? ?echo "$a -lt 100 -a $b -gt 15 : returns false"
fi
if [ $a -lt 100 -o $b -gt 100 ]
then
? ?echo "$a -lt 100 -o $b -gt 100 : returns true"
else
? ?echo "$a -lt 100 -o $b -gt 100 : returns false"
fi
if [ $a -lt 5 -o $b -gt 100 ]
then
? ?echo "$a -lt 100 -o $b -gt 100 : returns true"
else
? ?echo "$a -lt 100 -o $b -gt 100 : returns false"
fi
運行結(jié)果:
10 != 20 : a is not equal to b
10 -lt 100 -a 20 -gt 15 : returns true
10 -lt 100 -o 20 -gt 100 : returns true
10 -lt 5 -o 20 -gt 100 : returns false
布爾運算符列表
運算符 說明 舉例
! 非運算,表達(dá)式為 true 則返回 false,否則返回 true。 [ ! false ] 返回 true。
-o 或運算,有一個表達(dá)式為 true 則返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 與運算,兩個表達(dá)式都為 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。
字符串運算符
先來看一個例子:
#!/bin/sh
a="abc"
b="efg"
if [ $a = $b ]
then
? ?echo "$a = $b : a is equal to b"
else
? ?echo "$a = $b: a is not equal to b"
fi
if [ $a != $b ]
then
? ?echo "$a != $b : a is not equal to b"
else
? ?echo "$a != $b: a is equal to b"
fi
if [ -z $a ]
then
? ?echo "-z $a : string length is zero"
else
? ?echo "-z $a : string length is not zero"
fi
if [ -n $a ]
then
? ?echo "-n $a : string length is not zero"
else
? ?echo "-n $a : string length is zero"
fi
if [ $a ]
then
? ?echo "$a : string is not empty"
else
? ?echo "$a : string is empty"
fi
運行結(jié)果:
abc = efg: a is not equal to b
abc != efg : a is not equal to b
-z abc : string length is not zero
-n abc : string length is not zero
abc : string is not empty
字符串運算符列表
運算符 說明 舉例
= 檢測兩個字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 檢測兩個字符串是否相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 檢測字符串長度是否為0,為0返回 true。 [ -z $a ] 返回 false。
-n 檢測字符串長度是否為0,不為0返回 true。 [ -z $a ] 返回 true。
str 檢測字符串是否為空,不為空返回 true。 [ $a ] 返回 true。
文件測試運算符
文件測試運算符用于檢測 Unix 文件的各種屬性。
例如,變量 file 表示文件“/var/www/tutorialspoint/unix/test.sh”,它的大小為100字節(jié),具有 rwx 權(quán)限。下面的代碼,將檢測該文件的各種屬性:
#!/bin/sh
file="/var/www/tutorialspoint/unix/test.sh"
if [ -r $file ]
then
? ?echo "File has read access"
else
? ?echo "File does not have read access"
fi
if [ -w $file ]
then
? ?echo "File has write permission"
else
? ?echo "File does not have write permission"
fi
if [ -x $file ]
then
? ?echo "File has execute permission"
else
? ?echo "File does not have execute permission"
fi
if [ -f $file ]
then
? ?echo "File is an ordinary file"
else
? ?echo "This is sepcial file"
fi
if [ -d $file ]
then
? ?echo "File is a directory"
else
? ?echo "This is not a directory"
fi
if [ -s $file ]
then
? ?echo "File size is zero"
else
? ?echo "File size is not zero"
fi
if [ -e $file ]
then
? ?echo "File exists"
else
? ?echo "File does not exist"
fi
運行結(jié)果:
File has read access
File has write permission
File has execute permission
File is an ordinary file
This is not a directory
File size is zero
File exists
文件測試運算符列表
操作符 說明 舉例
-b file 檢測文件是否是塊設(shè)備文件,如果是,則返回 true。 [ -b $file ] 返回 false。
-c file 檢測文件是否是字符設(shè)備文件,如果是,則返回 true。 [ -b $file ] 返回 false。
-d file 檢測文件是否是目錄,如果是,則返回 true。 [ -d $file ] 返回 false。
-f file 檢測文件是否是普通文件(既不是目錄,也不是設(shè)備文件),如果是,則返回 true。 [ -f $file ] 返回 true。
-g file 檢測文件是否設(shè)置了 SGID 位,如果是,則返回 true。 [ -g $file ] 返回 false。
-k file 檢測文件是否設(shè)置了粘著位(Sticky Bit),如果是,則返回 true。 [ -k $file ] 返回 false。
-p file 檢測文件是否是具名管道,如果是,則返回 true。 [ -p $file ] 返回 false。
-u file 檢測文件是否設(shè)置了 SUID 位,如果是,則返回 true。 [ -u $file ] 返回 false。
-r file 檢測文件是否可讀,如果是,則返回 true。 [ -r $file ] 返回 true。
-w file 檢測文件是否可寫,如果是,則返回 true。 [ -w $file ] 返回 true。
-x file 檢測文件是否可執(zhí)行,如果是,則返回 true。 [ -x $file ] 返回 true。
-s file 檢測文件是否為空(文件大小是否大于0),不為空返回 true。 [ -s $file ] 返回 true。
-e file 檢測文件(包括目錄)是否存在,如果是,則返回 true。 [ -e $file ] 返回 true。
====
Shell注釋
以“#”開頭的行就是注釋,會被解釋器忽略。
sh里沒有多行注釋,只能每一行加一個#號。只能像這樣:
#--------------------------------------------
# 這是一個自動打ipa的腳本,基于webfrogs的ipa-build書寫:
# https://github.com/webfrogs/xcode_shell/blob/master/ipa-build
# 功能:自動為etao ios app打包,產(chǎn)出物為14個渠道的ipa包
# 特色:全自動打包,不需要輸入任何參數(shù)
#--------------------------------------------
##### 用戶配置區(qū) 開始 #####
#
#
# 項目根目錄,推薦將此腳本放在項目的根目錄,這里就不用改了
# 應(yīng)用名,確保和Xcode里Product下的target_name.app名字一致
#
##### 用戶配置區(qū) 結(jié)束 ?#####
如果在開發(fā)過程中,遇到大段的代碼需要臨時注釋起來,過一會兒又取消注釋,怎么辦呢?每一行加個#符號太費力了,可以把這一段要注釋的代碼用一對花括號括起來,定義成一個函數(shù),沒有地方調(diào)用這個函數(shù),這塊代碼就不會執(zhí)行,達(dá)到了和注釋一樣的效果。
微信公眾號:
====
Shell字符串
字符串是shell編程中最常用最有用的數(shù)據(jù)類型(除了數(shù)字和字符串,也沒啥其它類型好用了),字符串可以用單引號,也可以用雙引號,也可以不用引號。單雙引號的區(qū)別跟PHP類似。
單引號
str='this is a string'
單引號字符串的限制:
單引號里的任何字符都會原樣輸出,單引號字符串中的變量是無效的;
單引號字串中不能出現(xiàn)單引號(對單引號使用轉(zhuǎn)義符后也不行)。
雙引號
your_name='qinjx'
str="Hello, I know your are \"$your_name\"! \n"
雙引號的優(yōu)點:
雙引號里可以有變量
雙引號里可以出現(xiàn)轉(zhuǎn)義字符
拼接字符串
your_name="qinjx"
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
獲取字符串長度
string="abcd"
echo ${#string} #輸出 4
提取子字符串
string="alibaba is a great company"
echo ${string:1:4} #輸出liba
查找子字符串
string="alibaba is a great company"
echo `expr index "$string" is`
====
Shell數(shù)組:shell數(shù)組的定義、數(shù)組長度
Shell在編程方面比Windows批處理強(qiáng)大很多,無論是在循環(huán)、運算。
bash支持一維數(shù)組(不支持多維數(shù)組),并且沒有限定數(shù)組的大小。類似與C語言,數(shù)組元素的下標(biāo)由0開始編號。獲取數(shù)組中的元素要利用下標(biāo),下標(biāo)可以是整數(shù)或算術(shù)表達(dá)式,其值應(yīng)大于或等于0。
定義數(shù)組
在Shell中,用括號來表示數(shù)組,數(shù)組元素用“空格”符號分割開。定義數(shù)組的一般形式為:
? ? array_name=(value1 ... valuen)
例如:
array_name=(value0 value1 value2 value3)
或者
array_name=(
value0
value1
value2
value3
)
還可以單獨定義數(shù)組的各個分量:
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
可以不使用連續(xù)的下標(biāo),而且下標(biāo)的范圍沒有限制。
讀取數(shù)組
讀取數(shù)組元素值的一般格式是:
? ? ${array_name[index]}
例如:
valuen=${array_name[2]}
舉個例子:
#!/bin/sh
NAME[0]="Zara"
NAME[1]="Qadir"
NAME[2]="Mahnaz"
NAME[3]="Ayan"
NAME[4]="Daisy"
echo "First Index: ${NAME[0]}"
echo "Second Index: ${NAME[1]}"
運行腳本,輸出:
$./test.sh
First Index: Zara
Second Index: Qadir
使用@ 或 * 可以獲取數(shù)組中的所有元素,例如:
${array_name[*]}
${array_name[@]}
舉個例子:
#!/bin/sh
NAME[0]="Zara"
NAME[1]="Qadir"
NAME[2]="Mahnaz"
NAME[3]="Ayan"
NAME[4]="Daisy"
echo "First Method: ${NAME[*]}"
echo "Second Method: ${NAME[@]}"
運行腳本,輸出:
$./test.sh
First Method: Zara Qadir Mahnaz Ayan Daisy
Second Method: Zara Qadir Mahnaz Ayan Daisy
獲取數(shù)組的長度
獲取數(shù)組長度的方法與獲取字符串長度的方法相同,例如:
# 取得數(shù)組元素的個數(shù)
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得數(shù)組單個元素的長度
lengthn=${#array_name[n]}
====
Shell echo命令
echo是Shell的一個內(nèi)部指令,用于在屏幕上打印出指定的字符串。命令格式:
echo arg
您可以使用echo實現(xiàn)更復(fù)雜的輸出格式控制。
顯示轉(zhuǎn)義字符
echo "\"It is a test\""
結(jié)果將是:
"It is a test"
雙引號也可以省略。
顯示變量
name="OK"
echo "$name It is a test"
結(jié)果將是:
OK It is a test
同樣雙引號也可以省略。
如果變量與其它字符相連的話,需要使用大括號({ }):
mouth=8
echo "${mouth}-1-2009"
結(jié)果將是:
8-1-2009
顯示換行
echo "OK!\n"
echo "It is a test"
輸出:
OK!
It is a test
顯示不換行
echo "OK!\c"
echo "It is a test"
輸出:
OK!It si a test
顯示結(jié)果重定向至文件
echo "It is a test" > myfile
原樣輸出字符串
若需要原樣輸出字符串(不進(jìn)行轉(zhuǎn)義),請使用單引號。例如:
echo '$name\"'
顯示命令執(zhí)行結(jié)果
echo `date`
結(jié)果將顯示當(dāng)前日期
從上面可看出,雙引號可有可無,單引號主要用在原樣輸出中。
====
shell printf命令:格式化輸出語句
printf 命令用于格式化輸出, 是echo命令的增強(qiáng)版。它是C語言printf()庫函數(shù)的一個有限的變形,并且在語法上有些不同。
注意:printf 由 POSIX 標(biāo)準(zhǔn)所定義,移植性要比 echo 好。
如同 echo 命令,printf 命令也可以輸出簡單的字符串:
$printf "Hello, Shell\n"
Hello, Shell
$
printf 不像 echo 那樣會自動換行,必須顯式添加換行符(\n)。
printf 命令的語法:
printf ?format-string ?[arguments...]
format-string 為格式控制字符串,arguments 為參數(shù)列表。
printf()在C語言入門教程中已經(jīng)講到,功能和用法與 printf 命令類似,請查看:C語言格式輸出函數(shù)printf()詳解
這里僅說明與C語言printf()函數(shù)的不同:
printf 命令不用加括號
format-string 可以沒有引號,但最好加上,單引號雙引號均可。
參數(shù)多于格式控制符(%)時,format-string 可以重用,可以將所有參數(shù)都轉(zhuǎn)換。
arguments 使用空格分隔,不用逗號。
請看下面的例子:
# format-string為雙引號
$ printf "%d %s\n" 1 "abc"
1 abc
# 單引號與雙引號效果一樣?
$ printf '%d %s\n' 1 "abc"?
1 abc
# 沒有引號也可以輸出
$ printf %s abcdef
abcdef
# 格式只指定了一個參數(shù),但多出的參數(shù)仍然會按照該格式輸出,format-string 被重用
$ printf %s abc def
abcdef
$ printf "%s\n" abc def
abc
def
$ printf "%s %s %s\n" a b c d e f g h i j
a b c
d e f
g h i
j
# 如果沒有 arguments,那么 %s 用NULL代替,%d 用 0 代替
$ printf "%s and %d \n"?
and 0
# 如果以 %d 的格式來顯示字符串,那么會有警告,提示無效的數(shù)字,此時默認(rèn)置為 0
$ printf "The first program always prints'%s,%d\n'" Hello Shell
-bash: printf: Shell: invalid number
The first program always prints 'Hello,0'
$
注意,根據(jù)POSIX標(biāo)準(zhǔn),浮點格式%e、%E、%f、%g與%G是“不需要被支持”。這是因為awk支持浮點預(yù)算,且有它自己的printf語句。這樣Shell程序中需要將浮點數(shù)值進(jìn)行格式化的打印時,可使用小型的awk程序?qū)崿F(xiàn)。然而,內(nèi)建于bash、ksh93和zsh中的printf命令都支
持浮點格式。
====
Shell if else語句
if 語句通過關(guān)系運算符判斷表達(dá)式的真假來決定執(zhí)行哪個分支。Shell 有三種 if ... else 語句:
if ... fi 語句;
if ... else ... fi 語句;
if ... elif ... else ... fi 語句。
1) if ... else 語句
if ... else 語句的語法:
if [ expression ]
then
? ?Statement(s) to be executed if expression is true
fi
如果 expression 返回 true,then 后邊的語句將會被執(zhí)行;如果返回 false,不會執(zhí)行任何語句。
最后必須以 fi 來結(jié)尾閉合 if,fi 就是 if 倒過來拼寫,后面也會遇見。
注意:expression 和方括號([ ])之間必須有空格,否則會有語法錯誤。
舉個例子:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
? ?echo "a is equal to b"
fi
if [ $a != $b ]
then
? ?echo "a is not equal to b"
fi
運行結(jié)果:
a is not equal to b
2) if ... else ... fi 語句
if ... else ... fi 語句的語法:
if [ expression ]
then
? ?Statement(s) to be executed if expression is true
else
? ?Statement(s) to be executed if expression is not true
fi
如果 expression 返回 true,那么 then 后邊的語句將會被執(zhí)行;否則,執(zhí)行 else 后邊的語句。
舉個例子:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
? ?echo "a is equal to b"
else
? ?echo "a is not equal to b"
fi
執(zhí)行結(jié)果:
a is not equal to b
3) if ... elif ... fi 語句
if ... elif ... fi 語句可以對多個條件進(jìn)行判斷,語法為:
if [ expression 1 ]
then
? ?Statement(s) to be executed if expression 1 is true
elif [ expression 2 ]
then
? ?Statement(s) to be executed if expression 2 is true
elif [ expression 3 ]
then
? ?Statement(s) to be executed if expression 3 is true
else
? ?Statement(s) to be executed if no expression is true
fi
哪一個 expression 的值為 true,就執(zhí)行哪個 expression 后面的語句;如果都為 false,那么不執(zhí)行任何語句。
舉個例子:
#!/bin/sh
a=10
b=20
if [ $a == $b ]
then
? ?echo "a is equal to b"
elif [ $a -gt $b ]
then
? ?echo "a is greater than b"
elif [ $a -lt $b ]
then
? ?echo "a is less than b"
else
? ?echo "None of the condition met"
fi
運行結(jié)果:
a is less than b
if ... else 語句也可以寫成一行,以命令的方式來運行,像這樣:
if test $[2*3] -eq $[1+5]; then echo 'The two numbers are equal!'; fi;
if ... else 語句也經(jīng)常與 test 命令結(jié)合使用,如下所示:
num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
? ? echo 'The two numbers are equal!'
else
? ? echo 'The two numbers are not equal!'
fi
輸出:
The two numbers are equal!
test 命令用于檢查某個條件是否成立,與方括號([ ])類似。
====
Shell case esac語句
case ... esac 與其他語言中的 switch ... case 語句類似,是一種多分枝選擇結(jié)構(gòu)。
case 語句匹配一個值或一個模式,如果匹配成功,執(zhí)行相匹配的命令。case語句格式如下:
case 值 in
模式1)
? ? command1
? ? command2
? ? command3
? ? ;;
模式2)
? ? command1
? ? command2
? ? command3
? ? ;;
*)
? ? command1
? ? command2
? ? command3
? ? ;;
esac
case工作方式如上所示。取值后面必須為關(guān)鍵字 in,每一模式必須以右括號結(jié)束。取值可以為變量或常數(shù)。匹配發(fā)現(xiàn)取值符合某一模式后,其間所有命令開始執(zhí)行直至 ;;。;; 與其他語言中的 break 類似,意思是跳到整個 case 語句的最后。
取值將檢測匹配的每一個模式。一旦模式匹配,則執(zhí)行完匹配模式相應(yīng)命令后不再繼續(xù)其他模式。如果無一匹配模式,使用星號 * 捕獲該值,再執(zhí)行后面的命令。
下面的腳本提示輸入1到4,與每一種模式進(jìn)行匹配:
echo 'Input a number between 1 to 4'
echo 'Your number is:\c'
read aNum
case $aNum in
? ? 1) ?echo 'You select 1'
? ? ;;
? ? 2) ?echo 'You select 2'
? ? ;;
? ? 3) ?echo 'You select 3'
? ? ;;
? ? 4) ?echo 'You select 4'
? ? ;;
? ? *) ?echo 'You do not select a number between 1 to 4'
? ? ;;
esac
輸入不同的內(nèi)容,會有不同的結(jié)果,例如:
Input a number between 1 to 4
Your number is:3
You select 3
再舉一個例子:
#!/bin/bash
option="${1}"
case ${option} in
? ?-f) FILE="${2}"
? ? ? echo "File name is $FILE"
? ? ? ;;
? ?-d) DIR="${2}"
? ? ? echo "Dir name is $DIR"
? ? ? ;;
? ?*)?
? ? ? echo "`basename ${0}`:usage: [-f file] | [-d directory]"
? ? ? exit 1 # Command to come out of the program with status 1
? ? ? ;;
esac
運行結(jié)果:
$./test.sh
test.sh: usage: [ -f filename ] | [ -d directory ]
$ ./test.sh -f index.htm
$ vi test.sh
$ ./test.sh -f index.htm
File name is index.htm
$ ./test.sh -d unix
Dir name is unix
$
====
Shell for循環(huán)
與其他編程語言類似,Shell支持for循環(huán)。
for循環(huán)一般格式為:
for 變量 in 列表
do
? ? command1
? ? command2
? ? ...
? ? commandN
done
列表是一組值(數(shù)字、字符串等)組成的序列,每個值通過空格分隔。每循環(huán)一次,就將列表中的下一個值賦給變量。
in 列表是可選的,如果不用它,for 循環(huán)使用命令行的位置參數(shù)。
例如,順序輸出當(dāng)前列表中的數(shù)字:
for loop in 1 2 3 4 5
do
? ? echo "The value is: $loop"
done
運行結(jié)果:
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
順序輸出字符串中的字符:
for str in 'This is a string'
do
? ? echo $str
done
運行結(jié)果:
This is a string
顯示主目錄下以 .bash 開頭的文件:
#!/bin/bash
for FILE in $HOME/.bash*
do
? ?echo $FILE
done
運行結(jié)果:
/root/.bash_history
/root/.bash_logout
/root/.bash_profile
/root/.bashrc
====
Shell while循環(huán)
while循環(huán)用于不斷執(zhí)行一系列命令,也用于從輸入文件中讀取數(shù)據(jù);命令通常為測試條件。其格式為:
while command
do
? ?Statement(s) to be executed if command is true
done
命令執(zhí)行完畢,控制返回循環(huán)頂部,從頭開始直至測試條件為假。
以下是一個基本的while循環(huán),測試條件是:如果COUNTER小于5,那么返回 true。COUNTER從0開始,每次循環(huán)處理時,COUNTER加1。運行上述腳本,返回數(shù)字1到5,然后終止。
COUNTER=0
while [ $COUNTER -lt 5 ]
do
? ? COUNTER='expr $COUNTER+1'
? ? echo $COUNTER
done
運行腳本,輸出:
1
2
3
4
5
while循環(huán)可用于讀取鍵盤信息。下面的例子中,輸入信息被設(shè)置為變量FILM,按<Ctrl-D>結(jié)束循環(huán)。
echo 'type <CTRL-D> to terminate'
echo -n 'enter your most liked film: '
while read FILM
do
? ? echo "Yeah! great film the $FILM"
done
運行腳本,輸出類似下面:
type <CTRL-D> to terminate
enter your most liked film: Sound of Music
Yeah! great film the Sound of Music
====
Shell until循環(huán)
until 循環(huán)執(zhí)行一系列命令直至條件為 true 時停止。until 循環(huán)與 while 循環(huán)在處理方式上剛好相反。一般while循環(huán)優(yōu)于until循環(huán),但在某些時候,也只是極少數(shù)情況下,until 循環(huán)更加有用。
until 循環(huán)格式為:
until command
do
? ?Statement(s) to be executed until command is true
done
command 一般為條件表達(dá)式,如果返回值為 false,則繼續(xù)執(zhí)行循環(huán)體內(nèi)的語句,否則跳出循環(huán)。
例如,使用 until 命令輸出 0 ~ 9 的數(shù)字:
#!/bin/bash
a=0
until [ ! $a -lt 10 ]
do
? ?echo $a
? ?a=`expr $a + 1`
done
運行結(jié)果:
0
1
2
3
4
5
6
7
8
9
====
Shell break和continue命令
在循環(huán)過程中,有時候需要在未達(dá)到循環(huán)結(jié)束條件時強(qiáng)制跳出循環(huán),像大多數(shù)編程語言一樣,Shell也使用 break 和 continue 來跳出循環(huán)。
break命令
break命令允許跳出所有循環(huán)(終止執(zhí)行后面的所有循環(huán))。
下面的例子中,腳本進(jìn)入死循環(huán)直至用戶輸入數(shù)字大于5。要跳出這個循環(huán),返回到shell提示符下,就要使用break命令。
#!/bin/bash
while :
do
? ? echo -n "Input a number between 1 to 5: "
? ? read aNum
? ? case $aNum in
? ? ? ? 1|2|3|4|5) echo "Your number is $aNum!"
? ? ? ? ;;
? ? ? ? *) echo "You do not select a number between 1 to 5, game is over!"
? ? ? ? ? ? break
? ? ? ? ;;
? ? esac
done
在嵌套循環(huán)中,break 命令后面還可以跟一個整數(shù),表示跳出第幾層循環(huán)。例如:
break n
表示跳出第 n 層循環(huán)。
下面是一個嵌套循環(huán)的例子,如果 var1 等于 2,并且 var2 等于 0,就跳出循環(huán):
#!/bin/bash
for var1 in 1 2 3
do
? ?for var2 in 0 5
? ?do
? ? ? if [ $var1 -eq 2 -a $var2 -eq 0 ]
? ? ? then
? ? ? ? ?break 2
? ? ? else
? ? ? ? ?echo "$var1 $var2"
? ? ? fi
? ?done
done
如上,break 2 表示直接跳出外層循環(huán)。運行結(jié)果:
1 0
1 5
continue命令
continue命令與break命令類似,只有一點差別,它不會跳出所有循環(huán),僅僅跳出當(dāng)前循環(huán)。
對上面的例子進(jìn)行修改:
#!/bin/bash
while :
do
? ? echo -n "Input a number between 1 to 5: "
? ? read aNum
? ? case $aNum in
? ? ? ? 1|2|3|4|5) echo "Your number is $aNum!"
? ? ? ? ;;
? ? ? ? *) echo "You do not select a number between 1 to 5!"
? ? ? ? ? ? continue
? ? ? ? ? ? echo "Game is over!"
? ? ? ? ;;
? ? esac
done
運行代碼發(fā)現(xiàn),當(dāng)輸入大于5的數(shù)字時,該例中的循環(huán)不會結(jié)束,語句
echo "Game is over!"
永遠(yuǎn)不會被執(zhí)行。
同樣,continue 后面也可以跟一個數(shù)字,表示跳出第幾層循環(huán)。
再看一個 continue 的例子:
#!/bin/bash
NUMS="1 2 3 4 5 6 7"
for NUM in $NUMS
do
? ?Q=`expr $NUM % 2`
? ?if [ $Q -eq 0 ]
? ?then
? ? ? echo "Number is an even number!!"
? ? ? continue
? ?fi
? ?echo "Found odd number"
done
運行結(jié)果:
Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number
Number is an even number!!
Found odd number
====
Shell函數(shù):Shell函數(shù)返回值、刪除函數(shù)、在終端調(diào)用函數(shù)
函數(shù)可以讓我們將一個復(fù)雜功能劃分成若干模塊,讓程序結(jié)構(gòu)更加清晰,代碼重復(fù)利用率更高。像其他編程語言一樣,Shell 也支持函數(shù)。Shell 函數(shù)必須先定義后使用。
Shell 函數(shù)的定義格式如下:
function_name () {
? ? list of commands
? ? [ return value ]
}
如果你愿意,也可以在函數(shù)名前加上關(guān)鍵字 function:
function function_name () {
? ? list of commands
? ? [ return value ]
}
函數(shù)返回值,可以顯式增加return語句;如果不加,會將最后一條命令運行結(jié)果作為返回值。
Shell 函數(shù)返回值只能是整數(shù),一般用來表示函數(shù)執(zhí)行成功與否,0表示成功,其他值表示失敗。如果 return 其他數(shù)據(jù),比如一個字符串,往往會得到錯誤提示:“numeric argument required”。
如果一定要讓函數(shù)返回字符串,那么可以先定義一個變量,用來接收函數(shù)的計算結(jié)果,腳本在需要的時候訪問這個變量來獲得函數(shù)返回值。
先來看一個例子:
#!/bin/bash
# Define your function here
Hello () {
? ?echo "Url is http://see.xidian.edu.cn/cpp/shell/"
}
# Invoke your function
Hello
運行結(jié)果:
$./test.sh
Hello World
$
調(diào)用函數(shù)只需要給出函數(shù)名,不需要加括號。
再來看一個帶有return語句的函數(shù):
#!/bin/bash
funWithReturn(){
? ? echo "The function is to get the sum of two numbers..."
? ? echo -n "Input first number: "
? ? read aNum
? ? echo -n "Input another number: "
? ? read anotherNum
? ? echo "The two numbers are $aNum and $anotherNum !"
? ? return $(($aNum+$anotherNum))
}
funWithReturn
# Capture value returnd by last command
ret=$?
echo "The sum of two numbers is $ret !"
運行結(jié)果:
The function is to get the sum of two numbers...
Input first number: 25
Input another number: 50
The two numbers are 25 and 50 !
The sum of two numbers is 75 !
函數(shù)返回值在調(diào)用該函數(shù)后通過 $? 來獲得。
再來看一個函數(shù)嵌套的例子:
#!/bin/bash
# Calling one function from another
number_one () {
? ?echo "Url_1 is http://see.xidian.edu.cn/cpp/shell/"
? ?number_two
}
number_two () {
? ?echo "Url_2 is http://see.xidian.edu.cn/cpp/u/xitong/"
}
number_one
運行結(jié)果:
Url_1 is http://see.xidian.edu.cn/cpp/shell/
Url_2 is http://see.xidian.edu.cn/cpp/u/xitong/
像刪除變量一樣,刪除函數(shù)也可以使用 unset 命令,不過要加上 .f 選項,如下所示:
$unset .f function_name
如果你希望直接從終端調(diào)用函數(shù),可以將函數(shù)定義在主目錄下的 .profile 文件,這樣每次登錄后,在命令提示符后面輸入函數(shù)名字就可以立即調(diào)用。
====
Shell函數(shù)參數(shù)
在Shell中,調(diào)用函數(shù)時可以向其傳遞參數(shù)。在函數(shù)體內(nèi)部,通過 $n 的形式來獲取參數(shù)的值,例如,$1表示第一個參數(shù),$2表示第二個參數(shù)...
帶參數(shù)的函數(shù)示例:
#!/bin/bash
funWithParam(){
? ? echo "The value of the first parameter is $1 !"
? ? echo "The value of the second parameter is $2 !"
? ? echo "The value of the tenth parameter is $10 !"
? ? echo "The value of the tenth parameter is ${10} !"
? ? echo "The value of the eleventh parameter is ${11} !"
? ? echo "The amount of the parameters is $# !" ?# 參數(shù)個數(shù)
? ? echo "The string of the parameters is $* !" ?# 傳遞給函數(shù)的所有參數(shù)
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
運行腳本:
The value of the first parameter is 1 !
The value of the second parameter is 2 !
The value of the tenth parameter is 10 !
The value of the tenth parameter is 34 !
The value of the eleventh parameter is 73 !
The amount of the parameters is 12 !
The string of the parameters is 1 2 3 4 5 6 7 8 9 34 73 !"
注意,$10 不能獲取第十個參數(shù),獲取第十個參數(shù)需要${10}。當(dāng)n>=10時,需要使用${n}來獲取參數(shù)。
另外,還有幾個特殊變量用來處理參數(shù),前面已經(jīng)提到:
特殊變量 說明
$# 傳遞給函數(shù)的參數(shù)個數(shù)。
$* 顯示所有傳遞給函數(shù)的參數(shù)。
$@ 與$*相同,但是略有區(qū)別,請查看Shell特殊變量。
$? 函數(shù)的返回值。
====
Shell輸入輸出重定向:Shell Here Document,/dev/null文件
Unix 命令默認(rèn)從標(biāo)準(zhǔn)輸入設(shè)備(stdin)獲取輸入,將結(jié)果輸出到標(biāo)準(zhǔn)輸出設(shè)備(stdout)顯示。一般情況下,標(biāo)準(zhǔn)輸入設(shè)備就是鍵盤,標(biāo)準(zhǔn)輸出設(shè)備就是終端,即顯示器。
輸出重定向
命令的輸出不僅可以是顯示器,還可以很容易的轉(zhuǎn)移向到文件,這被稱為輸出重定向。
命令輸出重定向的語法為:
$ command > file
這樣,輸出到顯示器的內(nèi)容就可以被重定向到文件。
例如,下面的命令在顯示器上不會看到任何輸出:
$ who > users
打開 users 文件,可以看到下面的內(nèi)容:
$ cat users
oko ? ? ? ? tty01 ? Sep 12 07:30
ai ? ? ? ? ?tty15 ? Sep 12 13:32
ruth ? ? ? ?tty21 ? Sep 12 10:10
pat ? ? ? ? tty24 ? Sep 12 13:07
steve ? ? ? tty25 ? Sep 12 13:03
$
輸出重定向會覆蓋文件內(nèi)容,請看下面的例子:
$ echo line 1 > users
$ cat users
line 1
$
如果不希望文件內(nèi)容被覆蓋,可以使用 >> 追加到文件末尾,例如:
$ echo line 2 >> users
$ cat users
line 1
line 2
$
輸入重定向
和輸出重定向一樣,Unix 命令也可以從文件獲取輸入,語法為:
command < file
這樣,本來需要從鍵盤獲取輸入的命令會轉(zhuǎn)移到文件讀取內(nèi)容。
注意:輸出重定向是大于號(>),輸入重定向是小于號(<)。
例如,計算 users 文件中的行數(shù),可以使用下面的命令:
$ wc -l users
2 users
$
也可以將輸入重定向到 users 文件:
$ wc -l < users
2
$
注意:上面兩個例子的結(jié)果不同:第一個例子,會輸出文件名;第二個不會,因為它僅僅知道從標(biāo)準(zhǔn)輸入讀取內(nèi)容。
重定向深入講解
一般情況下,每個 Unix/Linux 命令運行時都會打開三個文件:
標(biāo)準(zhǔn)輸入文件(stdin):stdin的文件描述符為0,Unix程序默認(rèn)從stdin讀取數(shù)據(jù)。
標(biāo)準(zhǔn)輸出文件(stdout):stdout 的文件描述符為1,Unix程序默認(rèn)向stdout輸出數(shù)據(jù)。
標(biāo)準(zhǔn)錯誤文件(stderr):stderr的文件描述符為2,Unix程序會向stderr流中寫入錯誤信息。
默認(rèn)情況下,command > file 將 stdout 重定向到 file,command < file 將stdin 重定向到 file。
如果希望 stderr 重定向到 file,可以這樣寫:
$command 2 > file
如果希望 stderr 追加到 file 文件末尾,可以這樣寫:
$command 2 >> file
2 表示標(biāo)準(zhǔn)錯誤文件(stderr)。
如果希望將 stdout 和 stderr 合并后重定向到 file,可以這樣寫:
$command > file 2>&1
或
$command >> file 2>&1
如果希望對 stdin 和 stdout 都重定向,可以這樣寫:
$command < file1 >file2
command 命令將 stdin 重定向到 file1,將 stdout 重定向到 file2。?
全部可用的重定向命令列表
命令 說明
command > file 將輸出重定向到 file。
command < file 將輸入重定向到 file。
command >> file 將輸出以追加的方式重定向到 file。
n > file 將文件描述符為 n 的文件重定向到 file。
n >> file 將文件描述符為 n 的文件以追加的方式重定向到 file。
n >& m 將輸出文件 m 和 n 合并。
n <& m 將輸入文件 m 和 n 合并。
<< tag 將開始標(biāo)記 tag 和結(jié)束標(biāo)記 tag 之間的內(nèi)容作為輸入。
Here Document
Here Document 目前沒有統(tǒng)一的翻譯,這里暫譯為”嵌入文檔“。Here Document 是 Shell 中的一種特殊的重定向方式,它的基本的形式如下:
command << delimiter
? ? document
delimiter
它的作用是將兩個 delimiter 之間的內(nèi)容(document) 作為輸入傳遞給 command。
注意:
結(jié)尾的delimiter 一定要頂格寫,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 縮進(jìn)。
開始的delimiter前后的空格會被忽略掉。
下面的例子,通過 wc -l 命令計算 document 的行數(shù):
$wc -l << EOF
? ? This is a simple lookup program
? ? for good (and bad) restaurants
? ? in Cape Town.
EOF
3
$
也可以 將 Here Document 用在腳本中,例如:
#!/bin/bash
cat << EOF
This is a simple lookup program
for good (and bad) restaurants
in Cape Town.
EOF
運行結(jié)果:
This is a simple lookup program
for good (and bad) restaurants
in Cape Town.
下面的腳本通過 vi 編輯器將 document 保存到 test.txt 文件:
#!/bin/sh
filename=test.txt
vi $filename <<EndOfCommands
i
This file was created automatically from
a shell script
^[
ZZ
EndOfCommands
運行腳本:
$ sh test.sh
Vim: Warning: Input is not from a terminal
$
打開 test.txt,可以看到下面的內(nèi)容:
$ cat test.txt
This file was created automatically from
a shell script
$
/dev/null 文件
如果希望執(zhí)行某個命令,但又不希望在屏幕上顯示輸出結(jié)果,那么可以將輸出重定向到 /dev/null:
$ command > /dev/null
/dev/null 是一個特殊的文件,寫入到它的內(nèi)容都會被丟棄;如果嘗試從該文件讀取內(nèi)容,那么什么也讀不到。但是 /dev/null 文件非常有用,將命令的輸出重定向到它,會起到”禁止輸出“的效果。
如果希望屏蔽 stdout 和 stderr,可以這樣寫:
$ command > /dev/null 2>&1
====
Shell文件包含
像其他語言一樣,Shell 也可以包含外部腳本,將外部腳本的內(nèi)容合并到當(dāng)前腳本。
Shell 中包含腳本可以使用:
. filename
或
source filename
兩種方式的效果相同,簡單起見,一般使用點號(.),但是注意點號(.)和文件名中間有一空格。
例如,創(chuàng)建兩個腳本,一個是被調(diào)用腳本 subscript.sh,內(nèi)容如下:
url="http://see.xidian.edu.cn/cpp/view/2738.html"
一個是主文件 main.sh,內(nèi)容如下:
#!/bin/bash
. ./subscript.sh
echo $url
執(zhí)行腳本:
$chomd +x main.sh
./main.sh
http://see.xidian.edu.cn/cpp/view/2738.html
$
注意:被包含腳本不需要有執(zhí)行權(quán)限。
====
========
linux shell編程
<1>. 什么是shell?
shell扮演者操作系統(tǒng)內(nèi)核和用戶的中間人的角色,用戶通過鍵入shell command,然后shell通過解析用戶輸入,然后將請求轉(zhuǎn)發(fā)給操作系統(tǒng)的內(nèi)核進(jìn)行處理。
?1. 一個系統(tǒng)可以存在多個shell,可以通過cat /etc/shells命令查看系統(tǒng)中安裝的shell,不同的shell可能支持的命令語法是不相同的。
?2. 可以通過echo $SHELL查看當(dāng)前使用的shell?
<2>. 還是hello world程序?
首先使用vim編輯器(或者是linux下任意的文本編輯器)編寫文件helloshell.sh(沒有必要使用.sh后綴名):
?#!/bin/bash
echo "hello shell";
保存上面的文件,增加該文件的執(zhí)行權(quán)限:
?xuqiang@ubuntu:~/shell$ sudo chmod +x ./helloshell.sh
運行該shell程序:
?xuqiang@ubuntu:~/shell$ ./helloshell.sh
hello shell
通過上面的程序沒有什么實際的含義,但是通過第一個shell程序了解shell程序的執(zhí)行過程。?
<3>. shell中的變量?
3.1 系統(tǒng)變量
linnux下的shell腳本中的變量分為“系統(tǒng)變量”和“用戶自定義變量”,可以通過set命令查看那系統(tǒng)變量。
xuqiang@ubuntu:~/shell$ set
... 略去內(nèi)容
xuqiang@ubuntu:~/shell$ echo $HOME
/home/xuqiang
3.2 用戶定義變量
shell中用戶可以自定義變量,shell中的變量是沒有數(shù)據(jù)類型的,shell將根據(jù)當(dāng)前的環(huán)境自動進(jìn)行轉(zhuǎn)化,例如:
msg="hello world"?
上面的語句定義變量msg,并設(shè)置初始值是為hello world。
? Tip 1. 需要注意的是定義變量時,=兩邊是沒有空格的?
3.2.1 ?用戶定義變量規(guī)則
變量必須是以字母開頭,后跟字母或者是下劃線,變量的命名是大小寫敏感的,并且可以定義一個變量的值為NULL。
xuqiang@ubuntu:~/shell$ vech=
xuqiang@ubuntu:~/shell$ echo $vec
3.2.2 ?shell中如何使用變量
如果想要得到shell變量中存儲的值的話,需要在變量名前增加$符號,例如:
xuqiang@ubuntu:~/shell$ vech="value"
xuqiang@ubuntu:~/shell$ echo $vech # this will print the value of vech
value
xuqiang@ubuntu:~/shell$ echo vech # this will print the string "vech"
vech
3.2.3 全局變量 vs 局部變量?
默認(rèn)在shell中編寫的變量全部是局部變量,如果重新打開console的話,那么這些變量將全部丟失,全局的變量可以寫在文件~/.bashrc文件。
<4>. 控制結(jié)構(gòu)?
4.1 條件判定
在shell中條件判斷是通過test命令或者是[ ]實現(xiàn), 判斷條件如下:
數(shù)學(xué)運算:?
a -eq b :a == b
a -ne b : a != b
a -lt b : a < b
a -le b : a <= b
a -gt b : a > b
a -ge b : a >= b
string比較:
string1 = string2
string1 != string2
string1 : string1 不是NULL,或者是沒有定義
4.2 組合判定?
邏輯運算符:
! : not
exp1 -a exp2 : a && b
exp1 -o exp2 : exp1 || exp2?
4.3 if-else結(jié)構(gòu):
!/bin/sh#
# see whether arguments is positive
#
if [ $# -ne 1 ]
then
? ? ? ? echo "$0 : you must give/supply one integers"
? ? ? ? exit 1
fi
if test $1 -gt 0
then
? ? ? ? echo "$1 number is postivie"
else
? ? ? ? echo "$1 number is negative"
fi
#!/bin/sh ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
osch=0
echo "1. unix(sun os)"
echo "2. linux(red hat)"
echo -n "select your os choice [1 or 2] ?"
read osch
if [ $osch -eq 1 ?]
then
? ? ? ? echo "you pick up unix"
else
? ? ? ? #
? ? ? ? # nested if
? ? ? ? if [ $osch -eq 2 ]
? ? ? ? then
? ? ? ? ? ? ? ? echo "you pick up linux"
? ? ? ? else
? ? ? ? ? ? ? ? echo "what you donot like unix/linux"
? ? ? ? fi
fi
?
#!/bin/sh
#
# test the if .. elif .. else
#
if [ $1 -gt 0 ] ; then
? ? ? ? echo "$1 is positive"
elif [ $1 -lt 0 ] ; then
? ? ? ? echo "$1 is negative"
elif [ $1 -eq 0 ] ; then
? ? ? ? echo "$1 is zero"
else
? ? ? ? echo "$1 is not a number"
fi
?4.4 for
!/bin/sh
#
# test for
#
for i in 1 2 3 4 5
do
? ? ? ? echo "welcome $i times"
done
#!/bin/bash
for (( i = 0; i <= 5; i++ ))
do
? ? ? ? echo "welcome $i times"
done ?
?Tip 1. 注意程序中使用的shell腳本的類型
#!/bin/bash
for(( i = 1; i <= 5; i++ ?))
do
? ? ? ? for(( j = 1; j <= 5; ++j ))
? ? ? ? do
? ? ? ? ? ? ? ? echo -n "$i"
? ? ? ? done
# print a new line
? ? ? ? echo ""
done
4.5 while
#!/bin/bash
#
# test while loop
#
n=$1
i=0
while [ $i -le 10 ]
do
? ? ? ? echo "$n * $i = `expr $i \* $n`"
? ? ? ? i=`expr $i + 1`
done ??
4.6 case?
#!/bin/bash
#
# script to test case statement
#
action="update"
case $action in
? ? ? ? "update")
? ? ? ? ? ? ? ? echo "update the db"
? ? ? ? ? ? ? ? ;;
? ? ? ? "select")
? ? ? ? ? ? ? ? echo "select from db"
? ? ? ? ? ? ? ? ;;
? ? ? ? "delete")
? ? ? ? ? ? ? ? echo "delete from db"
? ? ? ? ? ? ? ? ;;
? ? ? ? *)
? ? ? ? ? ? ? ? echo "no action"
? ? ? ? ? ? ? ? ;;
esac ?
<5>. 函數(shù)?
5.1 函數(shù)聲明和定義
下面的程序中定義函數(shù)demo,向函數(shù)傳遞的所有參數(shù)表示為$*,第一個參數(shù)$1,第二個參數(shù)$2, 依次類推。
#!/bin/bash
function demo()
{
? ? ? ? echo "all function args : $*"
? ? ? ? echo "the first arg : $1"
? ? ? ? echo "the second arg : $2"
? ? ? ? echo "the third arg : $3"
}
# call the function
demo -f foo bar ?
5.2 函數(shù)調(diào)用(函數(shù)參數(shù))
shell中向函數(shù)傳遞參數(shù)是通過直接在函數(shù)調(diào)用后面添加參數(shù)完成,在函數(shù)中,傳遞的參數(shù)通過$1得到。
<6>. 腳本調(diào)試?
6.1 萬能的echo
shell的腳本調(diào)試是比價惡心的,這里僅僅是提供一些常規(guī)性的調(diào)試方法,最簡單的就是使用echo函數(shù)打印出變量的值從而達(dá)到調(diào)試目的。
6.2 兩個命令
shell腳本執(zhí)行可以通過./shell-filename.sh的形式執(zhí)行,另外的一種形式是通過bash ./shell-filename.sh的形式執(zhí)行,同時在執(zhí)行腳本時,可以使用-v或者是-x參數(shù)打印程序執(zhí)行信息。
-v:默認(rèn)打印shell讀取的內(nèi)容
-x:將命令“展開” 之后打印
例如對于下面的程序:
!/bin/sh
# for debug shell script
#
tot=`expr $1 + $2`
echo $tot
如果使用-v選項,結(jié)果如下:
?xuqiang@ubuntu:~/shell$ sh -v ./debug.sh 2 5
#!/bin/sh
#
# for debug shell script
#
tot=`expr $1 + $2`
echo $tot
7
如果是使用-x選項的話,結(jié)果:
?xuqiang@ubuntu:~/shell$ sh -x ./debug.sh 2 5
+ expr 2 + 5
+ tot=7
+ echo 7
7
========
Linux Shell編程入門
從程序員的角度來看, Shell本身是一種用C語言編寫的程序,從用戶的角度來看,Shell是用戶與Linux操作系統(tǒng)溝通的橋梁。用戶既可以輸入命令執(zhí)行,又可以利用 Shell腳本編程,完成更加復(fù)雜的操作。在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)默認(rèn)的Shell。在一般情況下,人們并不區(qū)分 Bourne?
Shell和Bourne Again Shell,所以,在下面的文字中,我們可以看到#!/bin/sh,它同樣也可以改為#!/bin/bash。
利用vi等文本編輯器編寫Shell腳本的格式是固定的,如下:
#!/bin/sh
#comments
Your commands go here
首行中的符號#!告訴系統(tǒng)其后路徑所指定的程序即是解釋此腳本文件的Shell程 序。如果首行沒有這句話,在執(zhí)行腳本文件的時候,將會出現(xiàn)錯誤。后續(xù)的部分就是主程序,Shell腳本像高級語言一樣,也有變量賦值,也有控制語句。除第 一行外,以#開頭的行就是注釋
行,直到此行的結(jié)束。如果一行未完成,可以在行尾加上",這個符號表明下一行與此行會合并為同一行。
編輯完畢,將腳本存盤為filename.sh,文件名后綴sh表明這是一個Bash腳本文件。執(zhí)行腳本的時候,要先將腳本文件的屬性改為可執(zhí)行的:
chmod +x filename.sh
執(zhí)行腳本的方法是:
./filename.sh
下面我們從經(jīng)典的“hello world”入手,看一看最簡單的Shell腳本的模樣。
#!/bin/sh
#print hello world in the console window
a = "hello world"
echo $a
Shell Script是一種弱類型語言,使用變量的時候無需首先聲明其類型。新的變量會在本地數(shù)據(jù)區(qū)分配內(nèi)存進(jìn)行存儲,這個變量歸當(dāng)前的Shell所有,任何子進(jìn) 程都不能訪問本地變量。這些變量與環(huán)境變量不同,環(huán)境變量被存儲在另一內(nèi)存區(qū),叫做用戶環(huán)境區(qū),這塊內(nèi)
存中的變量可以被子進(jìn)程訪問。變量賦值的方式是:
variable_name = variable_value
如果對一個已經(jīng)有值的變量賦值,新值將取代舊值。取值的時候要在變量名前加$,$variable_name可以在引號中使用,這一點和其他高級語言是明顯不同的。如果出現(xiàn)混淆的情況,可以使用花括號來區(qū)分,例如:
echo "Hi, $as"
就不會輸出“Hi, hello worlds”,而是輸出“Hi,”。這是因為Shell把$as當(dāng)成一個變量,而$as未被賦值,其值為空。正確的方法是:
echo "Hi, ${a}s"
單引號中的變量不會進(jìn)行變量替換操作。?
關(guān)于變量,還需要知道幾個與其相關(guān)的Linux命令。
env用于顯示用戶環(huán)境區(qū)中的變量及其取值;set用于顯示本地數(shù)據(jù)區(qū)和用戶環(huán)境區(qū)中的變量及其取值;unset用于刪除指定變量當(dāng)前的取值,該值將被指定為NULL;export命令用于將本地數(shù)據(jù)區(qū)中的變量轉(zhuǎn)移到用戶環(huán)境區(qū)。
下面我們來看一個更復(fù)雜的例子,結(jié)合這個例子,我們來講述Shell Script的語法。?
?1 #!/bin/bash
?2 # we have less than 3 arguments. Print the help text:
?3 if [ $# -lt 3 ]; then
?4 cat<<HELP
?5 ? ? ?ren -- renames a number of files using sed regular expressions
?6?
?7 ? ? ?USAGE: ren 'regexp' 'replacement' files
?8 ? ? ?EXAMPLE: rename all *.HTM files in *.html:
?9 ? ? ?ren 'HTM$' 'html' *.HTM
10?
11 HELP
12 ? ? ?exit 0
13 fi
14 OLD="$1"
15 NEW="$2"
16 # The shift command removes one argument from the list of
17 # command line arguments.
18 shift
19 shift
20 # $* contains now all the files:
21 for file in $*; do
22 if [ -f "$file" ]; then
23 ? ? newfile=`echo "$file" | sed ?"s/${OLD}/${NEW}/g"`
24 ? ? ? ? if [ -f "$newfile" ]; then
25 ? ? ? ? ? ? echo "ERROR: $newfile exists already"
26 ? ? ? ? else
27 ? ? ? ? ? ? echo "renaming $file to $newfile "
28 ? ? ? ? ? ? mv "$file" "$newfile"
29 ? ? ? ? fi
30 fi
31 done
我們從頭來看,前面兩行上一個例子中已經(jīng)解釋過了,從第三行開始,有新的內(nèi)容。if語句和其他編程語言相似,都是流程控制語句。它的語法是:
if …; then
…
elif …; then
…
else
…
fi
與其他語言不同,Shell Script中if語句的條件部分要以分號來分隔。第三行中的[]表示條件測試,常用的條件測試有下面幾種:
[ -f "$file" ] 判斷$file是否是一個文件
[ $a -lt 3 ] 判斷$a的值是否小于3,同樣-gt和-le分別表示大于或小于等于
[ -x "$file" ] 判斷$file是否存在且有可執(zhí)行權(quán)限,同樣-r測試文件可讀性
[ -n "$a" ] 判斷變量$a是否有值,測試空串用-z
[ "$a" = "$b" ] 判斷$a和$b的取值是否相等
[ cond1 -a cond2 ] 判斷cond1和cond2是否同時成立,-o表示cond1和cond2有一成立
要注意條件測試部分中的空格。在方括號的兩側(cè)都有空格,在-f、-lt、=等符號兩側(cè)同樣也有空格。如果沒有這些空格,Shell解釋腳本的時候就會出錯。
$#表示包括$0在內(nèi)的命令行參數(shù)的個數(shù)。在Shell中,腳本名稱本身是$0,剩下的依次是$0、$1、$2…、${10}、${11},等等。$*表示整個參數(shù)列表,不包括$0,也就是說不包括文件名的參數(shù)列表。
現(xiàn)在我們明白第三行的含義是如果腳本文件的參數(shù)少于三個,則執(zhí)行if和fi語句之間 的內(nèi)容。然后,從第四行到第十一行之間的內(nèi)容在Shell Script編程中被稱為Here文檔,Here文檔用于將多行文本傳遞給某一命令。Here文檔的格式是以<<開始,后跟一個字符串,在?
Here文檔結(jié)束的時候,這個字符串同樣也要出現(xiàn),表示文檔結(jié)束。在本例中,Here文檔被輸出給cat命令,也即將文檔內(nèi)容打印在屏幕上,起到顯示幫助 信息的作用。
第十二行的exit是Linux的命令,表示退出當(dāng)前進(jìn)程。在Shell腳本中可以使用所有的Linux命令,利用上面的cat和exit,從一方面來說,熟練使用Linux命令也可以大大減少Shell腳本的長度。
十四、十五兩句是賦值語句,分別將第一和第二參數(shù)賦值給變量OLD和NEW。緊接下來的兩句是注釋,注釋下面的兩條shift的作用是將參數(shù)列表中的第一個和第二個參數(shù)刪除,后面的參數(shù)依次變?yōu)樾碌牡谝缓偷诙?shù),注意參數(shù)列表原本也不包括$0。
然后,自二十一行到三十一行是一個循環(huán)語句。Shell Script中的循環(huán)有下面幾種格式:
while [ cond1 ] && { || } [ cond2 ] …; do
…
done
for var in …; do
…
done
for (( cond1; cond2; cond3 )) do
…
done
until [ cond1 ] && { || } [ cond2 ] …; do
…
done
在上面這些循環(huán)中,也可以使用類似C語言中的break和continue語句中斷 當(dāng)前的循環(huán)操作。第二十一行的循環(huán)是將參數(shù)列表中的參數(shù)一個一個地放入變量file中。然后進(jìn)入循環(huán),判斷file是否為一個文件,如果是文件的話,則用 sed命令搜索和生成新的文件名。sed基
本上可以看成一個查找替換程序,從標(biāo)準(zhǔn)輸入,例如管道讀入文本,并將結(jié)果輸出到標(biāo)準(zhǔn)輸出,sed使用正則表達(dá)式 進(jìn)行搜索。在第二十三行中,backtick(`)的作用是取出兩個backtick之間的命令輸出結(jié)果,在這里,也就是將結(jié)果取出賦給變量 newfile。此后,判斷
newfile是否已經(jīng)存在,否則就把file改成newfile。這樣我們就明白這個腳本的作用了,Shell Script編寫的其他腳本與此相似,只不過是語法和用法稍有不同而已。
通過這個例子我們明白了Shell Script的編寫規(guī)則,但還有幾件事情需要講述一下。
第一個,除了if語句之外,Shell Script中也有類似C語言中多分支結(jié)構(gòu)的case語句,它的語法是:
case var in
pattern 1 )
… ;;
pattern 2 )
… ;;
*)
… ;;
esac
我們再就下面一個例子,看看case語句的用法。
while getopts vc: OPTION
do
case $OPTION in
c) COPIES=$OPTARG
? ? ?ehco "$COPIES";;
v) echo "suyang";;
\?) exit 1;;
esac
done
上面的getopts類似于C語言提供的函數(shù)getopts,在Shell Script中,getopts經(jīng)常和while語句聯(lián)合起來使用。getopts的語法如下:
getopts option_string variable
option_string中包含一串單字符選項,若getopts在命令行參數(shù)中 發(fā)現(xiàn)了連字符,那么它會將連字符之后的字符與option_string進(jìn)行比較,若匹配成功,則把變量variable的值設(shè)為該選項,若無匹配,則把 變量的值設(shè)為?。有時候,選項還會帶一個值,例如-c5等,
這時要在option_string中該選項字母后面加上一個冒號,getopts發(fā)現(xiàn)冒號 后,會讀取該值,然后將該值放入特殊變量OPTARG中。這個命令比較復(fù)雜,如有需要,讀者可以詳細(xì)參閱Shell編寫的相關(guān)資料。
上面這個循環(huán)的作用就是依次取出腳本名稱后面的選項,進(jìn)行處理,如果輸入了非法選項,則進(jìn)入"?指定的部分,退出腳本程序。
第二個,Bash提供了一種用于交互式應(yīng)用的擴(kuò)展select,用戶可以從一組不同的值中進(jìn)行選擇。其語法如下:
select var in …; do
break;
done
例如,下面這段程序的輸出是:
#!/bin/bash
echo "Your choice?"
select var in "a" "b" "c"; do
break
done
echo $var
----------------------------
Your choice?
1) a
2) b
3) c
第三,Shell Script中也可以使用自定義的函數(shù),其語法形式如下:
functionname()
{
…
}
例如我們可以把上面第二個例子中第四到第十二行放入一個名為help函數(shù)體內(nèi),以后每次調(diào)用的時候直接寫help即可。函數(shù)中處理函數(shù)調(diào)用參數(shù)的方法是,直接用上面講過的$1、$2來分別表示第一、第二個參數(shù),用$*表示參數(shù)列表。
第四,我們也可以在Shell下調(diào)試Shell Script腳本,當(dāng)然最簡單的方法就是用echo輸出查看變量取值了。Bash也提供了真正的調(diào)試方法,就是執(zhí)行腳本的時候用-x參數(shù)。
sh ?x filename.sh
這會執(zhí)行腳本并顯示腳本中所有變量的取值,也可以使用參數(shù)-n,它并不執(zhí)行腳本,只是返回所有的語法錯誤。
========
理解Linux Shell和基本Shell腳本語言的小貼士
理解Linux Shell:
shell:一個命令行解釋器,通過執(zhí)行命令或腳本,幫助用戶與操作系統(tǒng)打交道。
process:進(jìn)程,用戶執(zhí)行的任務(wù)是進(jìn)程,但進(jìn)程不只是任務(wù)。
file:文件放在硬盤上,包含用戶所有的數(shù)據(jù)。
x-windows(又稱windows):Linux的一種模式,使屏幕包含多個視窗,讓用戶同時監(jiān)視多個任務(wù),方便地穿梭于任務(wù)之間,它是圖形化的。
text terminal:一種只能顯示文字或簡單圖形的視窗。
session:登入,建立會話;登出,注銷會話。
?
標(biāo)準(zhǔn)Linux發(fā)行版里的shell種類:
Bourne Shell:它是早期版本中的一種主要的shell,并且后來成為了標(biāo)準(zhǔn)。由貝爾實驗室的 Stephen Bourne 所寫。每個“類Unix”的系統(tǒng)里都至少有一種shell與之兼容。Bourne Shell 的程序命名為“sh”,一般放在 /bin/sh 。
C shell:由 Bill Joy 為 BSD 版本所寫。它的語法是模仿C語言的。它主要用于交互式操作,有很多交互式命令,而不太常用于寫腳本。
?
計算機(jī)的真正用途是簡化你的工作,你需要過程自動化,所以,你需要腳本。
腳本是命令的集合,保存在文件里。shell能讀取這些文件并且執(zhí)行里面的命令,就像執(zhí)行鍵盤發(fā)來的命令。shell還提供各種各樣的編程特性,使得腳本真心強(qiáng)大。
?
shell編程基礎(chǔ):
開啟終端,以獲取shell
看下你用著什么shell,運行:echo $SHELL
在Linux shell中,美元符號$代表一個變量
“echo”命令就是回復(fù)你鍵入任何東西
當(dāng)需要串聯(lián)幾個命令時,管線指令 “|” 就有用了
Linux命令有自己的語法,你的任何錯誤都不會被容忍和接受。如果你寫了條不規(guī)范的命令,當(dāng)然不會如愿執(zhí)行。
#!/bin/sh —— 這個叫環(huán)境。這句寫在shell腳本的首行,使得命令被發(fā)送到 /bin/sh 程序
?
關(guān)于shell 腳本:
shell腳本就是一個普通的文本文件,擴(kuò)展名是 ?.sh ,且能夠被執(zhí)行。
?
編寫和執(zhí)行腳本:
打開終端
“cd”命令,去到你想存放該腳本的地方
cd,回車,使你的提示符變成home目錄
touch hello.sh(這里我們的腳本命名為hello,而 .sh 是規(guī)定的)
vi hello.sh(nano hello.sh)用你最喜歡的編輯器,我用nano
chmod 744 hello.sh(使腳本可被執(zhí)行)
sh hello.sh 或者 ./hello.sh (執(zhí)行腳本)
?
編寫你第一個腳本:
#!/bin/bash
# My first script
?
echo "Hello World!"
如上所述,將這幾行東西,保存在文本文件里,使之可執(zhí)行,并執(zhí)行之。
輸出的樣子:
Hello World!
上面的代碼解釋如下。
#!/bin/bash ( 運行環(huán)境 )
# My first script ( 注釋,任何位于#后的東西都被注釋 )
echo “Hello World!” ( 腳本的主要部分 )
?
編寫你第二個腳本:
是時候?qū)懴乱粋€了。這個腳本會說出你的username和列出運行中的process
#! /bin/bash
echo "Hello $USER"
echo "Hey i am" $USER "and will be telling you about the current processes"
echo "Running processes List"
ps
如上所述,將這幾行東西,保存在文本文件里,文件叫啥名字都行,但記住擴(kuò)展名為 .sh ,使之可執(zhí)行,并執(zhí)行之。
Hello tecmint
Hey i am tecmint and will be telling you about the current processes
Running processes List
? PID TTY ? ? ? ? ?TIME CMD
?1111 pts/0 ? ?00:00:00 bash
?1287 pts/0 ? ?00:00:00 sh
?1288 pts/0 ? ?00:00:00 ps
覺得酷嗎?編寫腳本就像將想法轉(zhuǎn)變?yōu)榱鞒淌矫钜粯雍唵巍Mㄟ^內(nèi)置的過濾器和管道功能,shell 腳本擅長做簡單的文件系統(tǒng)操作。
當(dāng)你的需求更復(fù)雜,例如是功能、健壯、性能、效率等方面時,你就需要一個全能的語言了。
當(dāng)你已經(jīng)熟悉 C、PERL、PYTHON之類時,學(xué)習(xí)腳本語言就不覺得難了。
?
編寫你第三個腳本:
接著,來寫第三個也是本文最后一個腳本。這個腳本表現(xiàn)為一個互動式的腳本,何不親自執(zhí)行它,說下感覺怎樣?
#! /bin/bash
echo "Hey what's Your First Name?";
read a;
echo "welcome Mr./Mrs. $a, would you like to tell us, Your Last Name";
read b;
echo "Thanks Mr./Mrs. $a $b for telling us your name";
echo "*******************"
echo "Mr./Mrs. $b, it's time to say you good bye"
輸出的樣子:
Hey what's Your First Name?
Avishek
welcome Mr./Mrs. Avishek, would you like to tell us, Your Last Name
Kumar
Thanks Mr./Mrs. Avishek Kumar for telling us your name
******************************************************
Mr./Mrs. Kumar, it's time to say you good bye
其實,這事還沒完。我們只不過讓你淺嘗了一下。
========
25個好用的Shell腳本常用命令分享
?
1.列出所有目錄使用量,并按大小排序。
ls|xargs du -h|sort -rn?
#不遞歸下級目錄使用du -sh
2.查看文件排除以#開關(guān)和空白行,適合查看配置文件。
egrep -v "^#|^$" ?filename
sed '/#.*$/d; /^ *$/d'
3.刪除空格和空行。
sed '/^$/d' filename #刪除空行
sed 's/ //g' filename
sed 's/[[:space:]]//g' filename
4.刪除#后的注釋。
sed -i 's/#.*$//g' filename
5.踢出登錄的用戶,用who查看終端。
pkill -KILL -t pts/0
6.刪除空文件。
find / -type f -size 0 -exec rm -rf {} \;
7.查找進(jìn)程pid并kill。
pgrep nginx|xargs kill?
pidof nginx|xargs kill
8.獲取當(dāng)前IP地址,強(qiáng)大的awk,一個命令搞定。
ifconfig |awk -F"[ ]+|[:]" 'NR==2 {print $4}'
9.文本方式查看wtmp日志
utmpdump /var/log/wtmp
10.以內(nèi)存大小排序列出進(jìn)程
ps aux --sort=rss |sort -k 6 -rn
11.簡單web server列出當(dāng)前目錄文件,端口8000:
python -m SimpleHTTPServer
12.以管道輸入方式修改用戶密碼:
echo "password" |passwd –stdin root
12.生成SSH證書并復(fù)制到遠(yuǎn)端服務(wù)器:
ssh-keygen -y -f ~/.ssh/id_rsa && cat ~/.ssh/id_rsa.pub | ssh root@host "cat - >> ~/.ssh/authorized_keys"
13.shell下新建文件夾并進(jìn)入,以下加入bashrc:
mkcd ( ){
mkdir $1
cd $1
}
14.通過SSH快速備份文件到另一服務(wù)器:
tar zcvf - back/ | ssh root@www.jb51.net tar xzf - -C /root/back/
15.用wget下載整站:
wget -r -p -np -k http://www.jb51.net
#r遞歸 p下載所有文件 np不下載上級 k轉(zhuǎn)換相對鏈接
16.Kill整個進(jìn)程樹:
pstree -ap 10277|grep -oP '[0-9]{4,6}'|xargs kill -9
17.生成隨機(jī)字符:
cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1
18.使用awk導(dǎo)出最后一列非空的數(shù)據(jù):
awk -F "|" '{if($NF!="") print $NF}'
19.查找每行大于幾位數(shù)的數(shù)據(jù):
awk -F '' '{if(NF>6) print $0}'
20.獲取HTML頁面文本內(nèi)容:
lynx -dump www.jb51.net #包含頁面的URL
w3m -no-cookie -dump www.jb51.net
links -dump www.jb51.net #對中文內(nèi)容支持不好
21.端口重定向:
socat TCP4-LISTEN:1234,reuseaddr,fork, TCP4:www.baidu.com:80
22.行前或行后插入:
sed 'p;s/^.*$/--------/' file
awk '{print $0;print "-------"}' file
23.行首或行尾插入:
sed 's/^/new/g' file
sed 's/$/new/g' file
24.逐字換行:
awk -F "" '{for(i=1;i<=NF;i++) print $i}'
25.目錄中大量文件刪除:
ls | xargs rm
========
總結(jié)
以上是生活随笔為你收集整理的Linux Shell 编程学习总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中医香口方
- 下一篇: SQL Server 触发器学习总结