對於Shell腳本的編寫,可以使用任何文本編輯器,利用常用的文本編輯器、UltraEdit、Editplus、Gedit等都可以!
對於Shell腳本的書寫,約定俗成,第一行必須如下所示:(務必放在文件的第一行)
1
2
|
#!/bin/sh ... |
符號#!用來告訴系統執行該腳本的程序,這裏使用/bin/sh。編輯結束並保存;這裏最好使用“!/bin/bash”而不是“!/bin/sh”,如果使用tc shell改爲tcsh,其他類似。
如果要執行該腳本,必須先使用chmod命令打開腳本的執行權限,如下:
1
|
chmod +x
yourScriptName |
註釋: 在進行shell編程時,以 # 開頭的句子表示註釋;
變量: 在其他編程語言中你必須使用變量。在shell編程中,所有的變量都由字符串組成,並且大家不需要對變量進行聲明;當你要賦值給一個變量,首先熟悉如下知識;
1.需要給變量賦值時,可以這麼寫: 變量名=值
1-1. 首個字符必須爲字母(a-z,A-Z)
1-2 中間不能有空格,可以使用下劃線(_)
1-3 不能使用標點符號
1-4 不能使用bash裏的關鍵字(可用help命令查看保留關鍵字)
2. 要取用一個變量的值,只需在變量名前面加一個$ ( 注意: 給變量賦值的時候,不能在”=”兩邊留空格 )
3. echo 輸出(打印)
OK,書寫第一個完整Shell腳本:
1
2
3
4
5
6
|
#!/bin/sh #對變量賦值: himi= "HelloShell" #等號兩邊均不能有空格存在 #
現在打印變量a的內容: echo "A
is:" echo $himi |
OK,終端命令&輸出如下:
Last
login: Sat Apr 14 14:16:13 on ttys000
mac:~
Himi$ cd /Users/Himi/Desktop/
mac:Desktop
Himi$ chmod +x himi
mac:Desktop
Himi$ ./himi
A
is:
HelloShell
mac:Desktop
Himi$
需要大家注意的是有時候變量名可能會和其它文字混淆,比如:
#!/bin/sh
#對變量賦值:
himi=”test”
#等號兩邊均不能有空格存在
echo
$himi
echo
$himi A
echo
“A $himi”
#注意混淆
echo
“A $himi_OK”
#正確寫法:
echo
“A ${himi}_OK”
終端執行&打印:
1
2
3
4
5
6
7
|
mac:Desktop
Himi$ . /himi test test A A test A A
test_OK mac:Desktop
Himi$ |
童鞋們可以看到《 echo “A $himi_OK” 》這裏沒有正常打印出來,這是由於shell會去搜索變量himi_OK的值,而實際上這個變量此時並沒有值。這時,我們可以用花括號來告訴shell要打印的是himi變量;
還需要注意shell的默認賦值是字符串賦值。比如:
1
2
3
4
5
|
#!/bin/sh #字符串賦值: _int=19 _tot=$_int+89 echo
$_tot |
打印的_tot不是108 !而是 19+89 !出現此的原因就是因爲Shell默認是字符串賦值,應該需要計算應該先熟悉如下幾個知識:
let 表示數學運算
$[] 表示將中括號內的表達式作爲數學運算先計算結果再輸出。
expr 用於整數值運算,每一項用空格隔開
上面前兩種方式在bash下有效,在sh下會出錯。
OK,知道這些就可以重新將剛纔的腳本改寫成如下形式:
1
2
3
4
5
|
#!/bin/sh #字符串賦值: _int=19 _tot=$[$_int+89] echo $_tot |
3種形式輸出的結果都是 108 ;
將到這裏就不得不說下Shell的算術運算仂,對於Shell種的運算中,大家需要知道:
C shell只支持整數的運算;
運算符如下圖所示:
注意:
1.運算符兩側都必須有空格!!
2.C shell不支持浮點運算符(ps.如果您想要執行更加複雜的數學運算,可用UNIX的實用程序bc和nawk)
if語句:
[ -f "somefile" ] :判斷是否是一個文件
[ -x "/bin/ls" ] :判斷/bin/ls是否存在並有可執行權限
[ -n "$var" ] :判斷$var變量是否有值
[ "$a" = "$b" ] :判斷$a和$b是否相等
示例代碼:
1
2
3
4
5
6
7
8
9
10
11
|
#!/bin/sh varOne=1 varTwo=2 varThree=3 if [ "$varOne" = "$varTwo" ]; then echo "varTwo:$varTwo" elif [ "$varOne" = "$varThree" ]; then echo "varThree:$varThree" else echo "varOne:$varOne" fi |
務必注意,[]比較的時候其括號前後的空格別忘了! = 等號前後也要有空格也要注意;
&& 和 || 操作符:
示例代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#!/bin/sh varOne=1 varTwo=2 varThree=3 if [ "$varOne" = "$varThree" ]
|| [ "$varOne" = "$varTwo" ]; then echo "||
進入" else echo "No
|| 進入" fi if [ "$varOne" = "$varOne" ]
&& [ "$varOne" = "$varTwo" ]; then echo "&&
進入" else echo "No
&& 進入" fi |
case 語句:
須知:
case表達式可以用來匹配一個給定的字符串,而不是數字(可別和C語言裏的switch…case混淆)。
示例代碼:
1
2
3
4
5
6
7
8
9
10
11
12
|
#!/bin/sh ftype=` file "$1" ` #
Note ' and ` is different case "$ftype" in "$1:
Zip archive" *) unzip "$1" ;; "$1:
gzip compressed" *) gunzip "$1" ;; "$1:
bzip2 compressed" *) bunzip2 "$1" ;; *) echo "File
$1 can not be uncompressed with smartzip" ;; esac |
特殊變量$1,該變量包含有傳遞給該腳本的第一個參數值,也就是說,$1 就是字符串 articles.zip。
select 語句:
select表達式是bash的一種擴展應用,擅長於交互式場合。用戶可以從一組不同的值中進行選擇:
1
2
3
4
|
select var in ...
; do break ; done ....
now $var can be used .... |
示例代碼:
1
2
3
4
5
6
7
|
#!/bin/sh echo "What
is your favourite OS?" select var in "Linux" "Gnu
Hurd"
"Free BSD"
"Other" ; do break ; done echo "You
have selected $var" |
如果 以上腳本運行出現 select :NOT FOUND 將 #!/bin/sh 改爲 #!/bin/bash 該腳本的運行結果如下:
1
2
3
4
5
6
7
|
What
is your favourite OS? 1)
Linux 2)
Gnu Hurd 3)
Free BSD 4)
Other #?
1 You
have selected Linux |
while/for 循環:
示例代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/bin/sh varOne=1 varTwo=1 #
while while [ "$varOne" = "$varOne" ]; do echo "while
Done" break done #
for for varStr in H
I M I ; do echo "varStr
is $varStr" done |
輸出:
1
2
3
4
5
6
|
while Done varStr
is H varStr
is I varStr
is M varStr
is I localhost:Desktop
Himi$ |
select 語句:
須知:select表達式是bash的一種擴展應用,擅長於交互式場合。用戶可以從一組不同的值中進行選擇:
示例代碼:
1
2
3
4
5
6
|
#!/bin/sh echo "What
is your favourite?" select var in "iOS" "Android" "Himi" "Other" ; do break ; done echo "You
have selected $var" |
執行腳本後,等待用戶輸入,然後在終端輸入你的選擇,回車,如下顯示:
1
2
3
4
5
6
7
8
|
What
is your favourite? 1)
iOS 2)
Android 3)
Himi 4)
Other #?
3 You
have selected Himi localhost:Desktop
Himi$ |
函數:
如果你寫過比較複雜的腳本,就會發現可能在幾個地方使用了相同的代碼,這時如果用上函數,會方便很多。函數的大致樣子如下:
1
2
3
4
5
6
|
functionname() { #
inside the body $1 is the first argument given to the function #
$2 the second ... body } |
示例代碼:
1
2
3
4
5
6
7
|
#!/bin/sh himi() { echo "Function
is ok" exit 0 } himi |
腳本調試:
最簡單的調試方法當然是使用echo命令。你可以在任何懷疑出錯的地方用echo打印變量值,這也是大部分shell程序員花費80%的時間用於調試的原因。Shell腳本的好處在於無需重新編譯,而插入一個echo命令也不需要多少時間。shell也有一個真正的調試模式,如果腳本”strangescript”出錯,可以使用如下命令進行調試:
sh -x strangescript
上述命令會執行該腳本,同時顯示所有變量的值。shell還有一個不執行腳本只檢查語法的模式,命令如下:
sh -n your_script
執行一個Shell命令行時通常會自動打開3個標準文檔,即標準輸入文檔(stdin),通常對應終端的鍵盤;標準輸出文檔(stdout)和標準錯誤輸出文檔(stderr)都對應終端的屏幕。進程將從標準輸入文檔中得到輸入資料,將正常輸出資料輸出到標準輸出文檔,而將錯誤信息送到標準錯誤文檔中。
我們以cat命令爲例。cat命令的功能是從命令行給出的文件中讀取資料,並將這些資料直接送到標準輸出。若使用如下命令
# cat config
將會把文檔config的內容依次顯示到屏幕上。但是,如果cat的命令行中沒有參數,它就會從標準輸入中讀取資料,並將其送到標準輸出。例如:
# cat
Hello world
Hello world
Bye
Bye
用戶輸入的每一行都立刻被cat命令輸出到屏幕上。
另一個例子,命令sort按行讀入文檔正文(當命令行中沒有給出文件名時,表示從標準輸入讀入),將其排序,並將結果送到標準輸出。下面的例子是從標準輸入讀入一個採購單,並將其排序。
# sort
bananas
carrots
apples
apples
bananas
carrots
這時我們在屏幕上得到了已排序的採購單。直接使用標準輸入/輸出文檔存在以下問題:
輸入資料從終端輸入時,用戶輸入的資料只能用一次。下次再想用這些資料時就得重新輸入。而且在終端上輸入時,若輸入有誤修改起來不是很方便。輸出到終端屏幕上的信息只能看不能動。我們無法對此輸出做更多處理,如將輸出作爲另一命令的輸入進行進一步的處理等。爲了解決上述問題,Linux系統爲輸入、輸出的傳送引入了另外兩種機制,即輸入/輸出重定向和管道。
1.輸入重定向
輸入重定向是指把命令或可執行程序的標準輸入重定向到指定的文件中。也就是說,輸入可以不來自鍵盤,而來自一個指定的文件。所以說,輸入重定向主要用於改變一個命令的輸入源,特別是改變那些需要大量輸入的輸入源。例如,命令wc統計指定文檔包含的行數、單詞數和字符數。如果僅在命令行上鍵入:
# wc
wc將等待用戶告訴它統計什麼,這時Shell就好像死了一樣,從鍵盤鍵入的所有文本都出現在屏幕上,但並沒有什麼結果,直至按下【Ctrl+D】,wc纔將命令結果顯示在屏幕上。如果給出一個文檔名作爲wc命令的參數,如下例所示,wc將返回該文檔所包含的行數、單詞數和字符數。
# wc /etc/passwd
20 23 726 /etc/passwd
另一種把/etc/passwd文檔內容傳給wc命令的方法是重定向wc的輸入。輸入重定向的一般形式爲:命令<文件名。可以用下面的命令把wc命令的輸入重定向爲/etc/passwd文件:
# wc < /etc/passwd
20 23 726
另一種輸入重定向稱爲here文檔,它告訴Shell當前命令的標準輸入來自命令行。here文檔的重定向操作符使用<<。它將一對分隔符號(分隔符號是由<<符號後的單詞來定義的,本例中我們用eof來表示)之間的正文作爲標準輸入定向給命令。下例將一對分隔符號eof之間的正文作爲wc命令的輸入,統計出正文的行數、單詞數和字符數。
[root@mail root]# wc << eof
> hello
> world
> are you here?
> eof
3 5 26
在<<操作符後面,任何字符或單詞都可以作爲正文開始前的分隔符號,本例中使用eof就作爲分隔符號。here文檔的正文一直延續到遇見另一個分隔符號爲止。第二個分隔符號應出現在新行的開頭。這時here文檔的正文(不包括開始和結束的分隔符號)將重新定向送給命令wc作爲它的標準輸入。
由於大多數命令都以參數的形式在命令行上指定輸入檔的文件名,所以輸入重定向並不經常使用。儘管如此,當要使用一個不接受檔名作爲輸入參數的命令,而需要的輸入內容又存在一個文檔裏時,就能用輸入重定向解決問題。
2.輸出重定向
輸出重定向是指把命令(或可執行程序)的標準輸出或標準錯誤輸出重新定向到指定文檔中。這樣,該命令的輸出就不顯示在屏幕上,而是寫入到指定文檔中。
輸出重定向比輸入重定向更常用,很多情況下都可以使用這種功能。例如,如果某個命令的輸出很多,在屏幕上不能完全顯示,那麼將輸出重定向到一個文檔中,然後再用文本編輯器打開這個文檔,就可以查看輸出信息;如果想保存一個命令的輸出,也可以使用這種方法。還有,輸出重定向可以用於把一個命令的輸出當做另一個命令的輸入(還有一種更簡單的方法,就是使用管道,將在下面介紹)。
輸出重定向的一般形式爲:命令>文件名。例如
# ls > directory.out
# cat directory.out
ch1.doc ch2.doc ch3.doc chimp config mail/ test/
表示,將ls命令的輸出保存爲一個名爲directory.out的文件。
注:如果>符號後邊指定的文件已存在,那麼這個文件將被重寫。
爲避免輸出重定向中指定文件只能存放當前命令的輸出重定向的內容,Shell提供了輸出重定向的一種追加手段。輸出重定向與輸出重定向的功能非常相似,區別僅在於輸出重定向的功能是把命令(或可執行程序)的輸出結果追加到指定文件的最後,而該文件原有內容不被破壞。如果要將一條命令的輸出結果追加到指定文件的後面,可以使用追加重定向操作符>>。形式爲:命令>>文件名。例如:
# ls *.doc>>directory.out
# cat directory.out
ch1.doc ch2.doc ch3.doc chimp config mail/ test/
ch1.doc ch2.doc ch3.doc
和程序的標準輸出重定向一樣,程序的錯誤輸出也可以重新定向。使用符號2>(或追加符號2>>)表示對錯誤輸出設備重定向。例如下面的命令
# ls /usr/tmp 2> err.file
可在屏幕上看到程序的正常輸出結果,但又將程序的任何錯誤信息送到文件err.file中,以備將來檢查用。
還可以使用另一個輸出重定向操作符&>將標準輸出和錯誤輸出同時送到同一文件中。例如:
# ls /usr/tmp &> output.file
利用重定向將命令組合在一起,可實現系統單個命令不能提供的新功能。例如使用下面的命令序列
#s /usr/bin > /tmp/dir
# wc –w < /tmp/dir
459
可以統計了/usr/bin目錄下的文件個數。
3.管道
將一個程序或命令的輸出作爲另一個程序或命令的輸入有兩種方法,一種是通過一個暫存文件將兩個命令或程序結合在一起,例如上個例子中的/tmp/dir文件將ls和wc命令連在一起;另一種是Linux所提供的管道功能。這種方法比前一種方法更好。
管道可以把一系列命令連接起來,這意味着第一個命令的輸出會作爲第二個命令的輸入通過管道傳給第二個命令,第二個命令的輸出又會作爲第三個命令的輸入,以此類推。顯示在屏幕上的是管道行中最後一個命令的輸出(如果命令行中未使用輸出重定向)。
通過使用管道符“|”來創建一個管道行。用管道重寫上面的例子:
# ls /usr/bin|wc -w
1789
再如:
# cat sample.txt|grep "High"|wc –l
管道將cat命令(列出一個文件的內容)的輸出送給grep命令。grep命令在輸入裏查找單詞High,grep命令的輸出則是所有包含單詞High的行,這個輸出又被送給wc命令,wc命令統計出輸入中的行數。
假設sample.txt文件的內容如下:
Things to do today:
Low:Go grocery shopping
High:Return movie
High:Clear level 3 in Alien vs. Predator
Medium:Pick up clothes from dry cleaner
那麼該管道行的結果是2。
4.命令替換
命令替換和重定向有些相似,但區別在於命令替換是將一個命令的輸出作爲另外一個命令的參數。常用命令格式爲:
# command1 `command2`
其中,command2的輸出將作爲command1的參數。需要注意的是這裏的 ` 符號,被它括起來的內容將作爲命令執行,執行後的結果作爲command1的參數。例如:
$ cd `pwd`
該命令將pwd命令列出的目錄作爲cd命令的參數,結果仍然停留在當前目錄下。
5.前臺和後臺
在Shell下面,一個新產生的進程可以通過用命令後面的符號“;”和“&”來分別以前臺和後臺的方式來執行,語法如下:
# command
產生一個前臺的進程,下一個命令需等該命令運行結束後才能輸入。
# command &
產生一個後臺的進程,此進程在後臺運行的同時,可以輸入其他的命令。