欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Shell腳本

系統 1886 0

http://learn.akae.cn/media/ch31.html

目錄

1. Shell的歷史
2. Shell如何執行命令
2.1. 執行交互式命令
2.2. 執行腳本
3. Shell的基本語法
3.1. 變量
3.2. 文件名代換(Globbing):* ? []
3.3. 命令代換:`或 $()
3.4. 算術代換:$(())
3.5. 轉義字符\
3.6. 單引號
3.7. 雙引號
4. bash啟動腳本
4.1. 作為交互登錄Shell啟動,或者使用--login參數啟動
4.2. 以交互非登錄Shell啟動
4.3. 非交互啟動
4.4. 以sh命令啟動
5. Shell腳本語法
5.1. 條件測試:test [
5.2. if/then/elif/else/fi
5.3. case/esac
5.4. for/do/done
5.5. while/do/done
5.6. 位置參數和特殊變量
5.7. 函數
6. Shell腳本的調試方法

1.?Shell的歷史? 請點評

Shell的作用是解釋執行用戶的命令,用戶輸入一條命令,Shell就解釋執行一條,這種方式稱為交互式(Interactive) ,Shell還有一種執行命令的方式稱為批處理(Batch) ,用戶事先寫一個Shell腳本(Script) ,其中有很多條命令,讓Shell一次把這些命令執行完,而不必一條一條地敲命令。Shell腳本和編程語言很相似,也有變量和流程控制語句,但Shell腳本是解釋執行的,不需要編譯,Shell程序從腳本中一行一行讀取并執行這些命令,相當于一個用戶把腳本中的命令一行一行敲到Shell提示符下執行。

由于歷史原因,UNIX系統上有很多種Shell:

  1. sh (Bourne Shell):由Steve Bourne開發,各種UNIX系統都配有 sh 。

  2. csh (C Shell):由Bill Joy開發,隨BSD UNIX發布,它的流程控制語句很像C語言,支持很多Bourne Shell所不支持的功能:作業控制,命令歷史,命令行編輯。

  3. ksh (Korn Shell):由David Korn開發,向后兼容 sh 的功能,并且添加了 csh 引入的新功能,是目前很多UNIX系統標準配置的Shell,在這些系統上 /bin/sh 往往是指向 /bin/ksh 的符號鏈接。

  4. tcsh (TENEX C Shell):是 csh 的增強版本,引入了命令補全等功能,在FreeBSD、Mac OS X等系統上替代了 csh 。

  5. bash (Bourne Again Shell):由GNU開發的Shell,主要目標是與POSIX標準保持一致,同時兼顧對 sh 的兼容, bash csh ksh 借鑒了很多功能,是各種Linux發行版標準配置的Shell,在Linux系統上 /bin/sh 往往是指向 /bin/bash 的符號鏈接 [ 38 ] 。雖然如此, bash sh 還是有很多不同的,一方面, bash 擴展了一些命令和參數,另一方面, bash 并不完全和 sh 兼容,有些行為并不一致,所以 bash 需要模擬 sh 的行為:當我們通過 sh 這個程序名啟動 bash 時, bash 可以假裝自己是 sh ,不認擴展的命令,并且行為與 sh 保持一致。

文件 /etc/shells 給出了系統中所有已知(不一定已安裝)的Shell,除了上面提到的Shell之外還有很多變種。

      # /etc/shells: valid login shells

/bin/csh

/bin/sh

/usr/bin/es

/usr/bin/ksh

/bin/ksh

/usr/bin/rc

/usr/bin/tcsh

/bin/tcsh

/usr/bin/esh

/bin/dash

/bin/bash

/bin/rbash

/usr/bin/screen
    

用戶的默認Shell設置在 /etc/passwd 文件中,例如下面這行對用戶mia的設置:

      mia:L2NOfqdlPrHwE:504:504:Mia Maya:/home/mia:/bin/bash
    

用戶mia從字符終端登錄或者打開圖形終端窗口時就會自動執行 /bin/bash 。如果要切換到其它Shell,可以在命令行輸入程序名,例如:

      ~$ sh(在bash提示符下輸入sh命令)

$(出現sh的提示符)

$(按Ctrl-d或者輸入exit命令)

~$(回到bash提示符)

~$(再次按Ctrl-d或者輸入exit命令會退出登錄或者關閉圖形終端窗口)
    

本章只介紹 bash sh 的用法和相關語法,不介紹其它Shell。所以下文提到Shell都是指 bash sh 。



[ 38 ]? 最新的發行版有一些變化,例如Ubuntu 7.10的 /bin/sh 是指向 /bin/dash 的符號鏈接, dash 也是一種類似 bash 的Shell。

          $ ls /bin/sh /bin/dash -l

-rwxr-xr-x 1 root root 79988 2008-03-12 19:22 /bin/dash

lrwxrwxrwx 1 root root     4 2008-07-04 05:58 /bin/sh -> dash
          

2.?Shell如何執行命令? 請點評

2.1.?執行交互式命令? 請點評

用戶在命令行輸入命令后,一般情況下Shell會 fork exec 該命令,但是Shell的內建命令例外,執行內建命令相當于調用Shell進程中的一個函數,并不創建新的進程。以前學過的 cd 、 alias 、 umask 、 exit 等命令即是內建命令,凡是用 which 命令查不到程序文件所在位置的命令都是內建命令,內建命令沒有單獨的man手冊,要在man手冊中查看內建命令,應該

            $ man bash-builtins
          

本節會介紹很多內建命令,如 export 、 shift 、 if 、 eval 、 [ 、 for while 等等。內建命令雖然不創建新的進程,但也會有Exit Status,通常也用0表示成功非零表示失敗,雖然內建命令不創建新的進程,但執行結束后也會有一個狀態碼,也可以用特殊變量 $? 讀出。

1、在完成 第?5?節 “練習:實現簡單的Shell” 時也許有的讀者已經試過了,在自己實現的Shell中不能執行 cd 命令,因為 cd 是一個內建命令,沒有程序文件,不能用 exec 執行?,F在請完善該程序,實現 cd 命令的功能,用 chdir(2) 函數可以改變進程的當前工作目錄。

2、思考一下,為什么 cd 命令要實現成內建命令?可不可以實現一個獨立的 cd 程序,例如 /bin/cd ,就像 /bin/ls 一樣?

2.2.?執行腳本? 請點評

首先編寫一個簡單的腳本,保存為 script.sh

例?31.1.?簡單的Shell腳本

                #! /bin/sh



cd ..

ls
              

Shell腳本中用 # 表示注釋,相當于C語言的 // 注釋。但如果 # 位于第一行開頭,并且是 #! (稱為Shebang )則例外,它表示該腳本使用后面指定的解釋器 /bin/sh 解釋執行。如果把這個腳本文件加上可執行權限然后執行:

            $ chmod +x script.sh

$ ./script.sh
          

Shell會 fork 一個子進程并調用 exec 執行 ./script.sh 這個程序, exec 系統調用應該把子進程的代碼段替換成 ./script.sh 程序的代碼段,并從它的 _start 開始執行。然而 script.sh 是個文本文件,根本沒有代碼段和 _start 函數,怎么辦呢?其實 exec 還有另外一種機制,如果要執行的是一個文本文件,并且第一行用Shebang指定了解釋器,則用解釋器程序的代碼段替換當前進程,并且從解釋器的 _start 開始執行,而這個文本文件被當作命令行參數傳給解釋器。因此,執行上述腳本相當于執行程序

            $ /bin/sh ./script.sh
          

以這種方式執行不需要 script.sh 文件具有可執行權限。再舉個例子,比如某個 sed 腳本的文件名是 script ,它的開頭是

            #! /bin/sed -f
          

執行 ./script 相當于執行程序

            $ /bin/sed -f ./script.sh
          

以上介紹了兩種執行Shell腳本的方法:

            $ ./script.sh

$ sh ./script.sh
          

這兩種方法本質上是一樣的,執行上述腳本的步驟為:

圖?31.1.?Shell腳本的執行過程


  1. 交互Shell( bash fork / exec 一個子Shell( sh )用于執行腳本,父進程 bash 等待子進程 sh 終止。

  2. sh 讀取腳本中的 cd .. 命令,調用相應的函數執行內建命令,改變當前工作目錄為上一級目錄。

  3. sh 讀取腳本中的 ls 命令, fork / exec 這個程序,列出當前工作目錄下的文件, sh 等待 ls 終止。

  4. ls 終止后, sh 繼續執行,讀到腳本文件末尾, sh 終止。

  5. sh 終止后, bash 繼續執行,打印提示符等待用戶輸入。

如果將命令行下輸入的命令用()括號括起來,那么也會 fork 出一個子Shell執行小括號中的命令,一行中可以輸入由分號;隔開的多個命令,比如:

            $ (cd ..;ls -l)
          

和上面兩種方法執行Shell腳本的效果是相同的, cd .. 命令改變的是子Shell的 PWD ,而不會影響到交互式Shell。然而命令

            $ cd ..;ls -l
          

則有不同的效果, cd .. 命令是直接在交互式Shell下執行的,改變交互式Shell的 PWD ,然而這種方式相當于這樣執行Shell腳本:

            $ source ./script.sh
          

或者

            $ . ./script.sh
          

source 或者 . 命令是Shell的內建命令,這種方式也不會創建子Shell,而是直接在交互式Shell下逐行執行腳本中的命令。

3.?Shell的基本語法? 請點評

3.1.?變量? 請點評

按照慣例,Shell變量由全大寫字母加下劃線組成,有兩種類型的Shell變量:

環境變量

第?2?節 “環境變量” 中講過,環境變量可以從父進程傳給子進程,因此Shell進程的環境變量可以從當前Shell進程傳給 fork 出來的子進程。用 printenv 命令可以顯示當前Shell進程的環境變量。

本地變量

只存在于當前Shell進程,用 set 命令可以顯示當前Shell進程中定義的所有變量(包括本地變量和環境變量)和函數。

環境變量是任何進程都有的概念,而本地變量是Shell特有的概念。在Shell中,環境變量和本地變量的定義和用法相似。在Shell中定義或賦值一個變量:

                $ VARNAME=value
              

注意等號兩邊都不能有空格,否則會被Shell解釋成命令和命令行參數。

一個變量定義后僅存在于當前Shell進程,它是本地變量,用 export 命令可以把本地變量導出為環境變量,定義和導出環境變量通??梢砸徊酵瓿桑?

                $ export VARNAME=value
              

也可以分兩步完成:

                $ VARNAME=value

$ export VARNAME
              

unset 命令可以刪除已定義的環境變量或本地變量。

                $ unset VARNAME
              

如果一個變量叫做 VARNAME ,用 ${VARNAME} 可以表示它的值,在不引起歧義的情況下也可以用 $VARNAME 表示它的值。通過以下例子比較這兩種表示法的不同:

                $ echo $SHELL

$ echo $SHELLabc

$ echo $SHELL abc

$ echo ${SHELL}abc
              

注意,在定義變量時不用$,取變量值時要用$。和C語言不同的是,Shell變量不需要明確定義類型,事實上Shell變量的值都是字符串,比如我們定義 VAR=45 ,其實 VAR 的值是字符串 45 而非整數。Shell變量不需要先定義后使用,如果對一個沒有定義的變量取值,則值為空字符串。

3.2.?文件名代換(Globbing) :* ? []? 請點評

這些用于匹配的字符稱為通配符(Wildcard) ,具體如下:

表?31.1.?通配符

* 匹配0個或多個任意字符
? 匹配一個任意字符
[若干字符] 匹配方括號中任意一個字符的一次出現

                $ ls /dev/ttyS*

$ ls ch0?.doc

$ ls ch0[0-2].doc

$ ls ch[012][0-9].doc
              

注意,Globbing所匹配的文件名是由Shell展開的,也就是說在參數還沒傳給程序之前已經展開了,比如上述 ls ch0[012].doc 命令,如果當前目錄下有 ch00.doc ch02.doc ,則傳給 ls 命令的參數實際上是這兩個文件名,而不是一個匹配字符串。

3.3.?命令代換:`或 $()? 請點評

由反引號括起來的也是一條命令,Shell先執行該命令,然后將輸出結果立刻代換到當前命令行中。例如定義一個變量存放 date 命令的輸出:

                $ DATE=`date`

$ echo $DATE
              

命令代換也可以用 $() 表示:

                $ DATE=$(date)
              

3.4.?算術代換:$(())? 請點評

用于算術計算, $(()) 中的Shell變量取值將轉換成整數,例如:

                $ VAR=45

$ echo $(($VAR+3))
              

$(()) 中只能用+-*/和()運算符,并且只能做整數運算。

3.5.?轉義字符\? 請點評

和C語言類似,\在Shell中被用作轉義字符,用于去除緊跟其后的單個字符的特殊意義(回車除外),換句話說,緊跟其后的字符取字面值。例如:

                $ echo $SHELL

/bin/bash

$ echo \$SHELL

$SHELL

$ echo \\

\
              

比如創建一個文件名為“ $ $ ”的文件可以這樣:

                $ touch \$\ \$
              

還有一個字符雖然不具有特殊含義,但是要用它做文件名也很麻煩,就是-號。如果要創建一個文件名以-號開頭的文件,這樣是不行的:

                $ touch -hello

touch: invalid option -- h

Try `touch --help' for more information.
              

即使加上\轉義也還是報錯:

                $ touch \-hello

touch: invalid option -- h

Try `touch --help' for more information.
              

因為各種UNIX命令都把-號開頭的命令行參數當作命令的選項,而不會當作文件名。如果非要處理以-號開頭的文件名,可以有兩種辦法:

                $ touch ./-hello
              

或者

                $ touch -- -hello
              

\還有一種用法,在\后敲回車表示續行,Shell并不會立刻執行命令,而是把光標移到下一行,給出一個續行提示符>,等待用戶繼續輸入,最后把所有的續行接到一起當作一個命令執行。例如:

                $ ls \

> -l

(ls -l命令的輸出)
              

3.6.?單引號? 請點評

和C語言不一樣,Shell腳本中的單引號和雙引號一樣都是字符串的界定符(雙引號下一節介紹),而不是字符的界定符。單引號用于保持引號內所有字符的字面值,即使引號內的\和回車也不例外,但是字符串中不能出現單引號。如果引號沒有配對就輸入回車,Shell會給出續行提示符,要求用戶把引號配上對。例如:

                $ echo '$SHELL'

$SHELL

$ echo 'ABC\(回車)

> DE'(再按一次回車結束命令)

ABC\

DE
              

3.7.?雙引號? 請點評

雙引號用于保持引號內所有字符的字面值(回車也不例外),但以下情況除外:

  • $加變量名可以取變量的值

  • 反引號仍表示命令替換

  • \$表示$的字面值

  • \`表示`的字面值

  • \"表示"的字面值

  • \\表示\的字面值

  • 除以上情況之外,在其它字符前面的\無特殊含義,只表示字面值

                $ echo "$SHELL"

/bin/bash

$ echo "`date`"

Sun Apr 20 11:22:06 CEST 2003

$ echo "I'd say: \"Go for it\""

I'd say: "Go for it"

$ echo "\"(回車)

>"(再按一次回車結束命令)

"



$ echo "\\"

\
                

4.?bash啟動腳本? 請點評

啟動腳本是 bash 啟動時自動執行的腳本。用戶可以把一些環境變量的設置和 alias 、 umask 設置放在啟動腳本中,這樣每次啟動Shell時這些設置都自動生效。思考一下, bash 在執行啟動腳本時是以 fork 子Shell方式執行的還是以 source 方式執行的?

啟動bash的方法不同,執行啟動腳本的步驟也不相同,具體可分為以下幾種情況。

4.1.?作為交互登錄Shell啟動,或者使用--login參數啟動? 請點評

交互Shell是指用戶在提示符下輸命令的Shell而非執行腳本的Shell,登錄Shell就是在輸入用戶名和密碼登錄后得到的Shell,比如從字符終端登錄或者用 telnet / ssh 從遠程登錄,但是從圖形界面的窗口管理器登錄之后會顯示桌面而不會產生登錄Shell(也不會執行啟動腳本),在圖形界面下打開終端窗口得到的Shell也不是登錄Shell。

這樣啟動 bash 會自動執行以下腳本:

  1. 首先執行 /etc/profile ,系統中每個用戶登錄時都要執行這個腳本,如果系統管理員希望某個設置對所有用戶都生效,可以寫在這個腳本里

  2. 然后依次查找當前用戶主目錄的 ~/.bash_profile 、 ~/.bash_login ~/.profile 三個文件,找到第一個存在并且可讀的文件來執行,如果希望某個設置只對當前用戶生效,可以寫在這個腳本里,由于這個腳本在 /etc/profile 之后執行, /etc/profile 設置的一些環境變量的值在這個腳本中可以修改,也就是說,當前用戶的設置可以覆蓋(Override) 系統中全局的設置。 ~/.profile 這個啟動腳本是 sh 規定的, bash 規定首先查找以 ~/.bash_ 開頭的啟動腳本,如果沒有則執行 ~/.profile ,是為了和 sh 保持一致。

  3. 順便一提,在退出登錄時會執行 ~/.bash_logout 腳本(如果它存在的話)。

4.2.?以交互非登錄Shell啟動? 請點評

比如在圖形界面下開一個終端窗口,或者在登錄Shell提示符下再輸入 bash 命令,就得到一個交互非登錄的Shell,這種Shell在啟動時自動執行 ~/.bashrc 腳本。

為了使登錄Shell也能自動執行 ~/.bashrc ,通常在 ~/.bash_profile 中調用 ~/.bashrc

                    if [ -f ~/.bashrc ]; then

    . ~/.bashrc

fi
                  

這幾行的意思是如果 ~/.bashrc 文件存在則 source 它。多數Linux發行版在創建帳戶時會自動創建 ~/.bash_profile ~/.bashrc 腳本, ~/.bash_profile 中通常都有上面這幾行。所以,如果要在啟動腳本中做某些設置,使它在圖形終端窗口和字符終端的Shell中都起作用,最好就是在 ~/.bashrc 中設置。

下面做一個實驗,在 ~/.bashrc 文件末尾添加一行(如果這個文件不存在就創建它):

                    export PATH=$PATH:/home/akaedu
                  

然后關掉終端窗口重新打開,或者從字符終端 logout 之后重新登錄,現在主目錄下的程序應該可以直接輸程序名運行而不必輸入路徑了,例如:

                    ~$ a.out
                  

就可以了,而不必

                    ~$ ./a.out
                  

為什么登錄Shell和非登錄Shell的啟動腳本要區分開呢?最初的設計是這樣考慮的,如果從字符終端或者遠程登錄,那么登錄Shell是該用戶的所有其它進程的父進程,也是其它子Shell的父進程,所以環境變量在登錄Shell的啟動腳本里設置一次就可以自動帶到其它非登錄Shell里,而Shell的本地變量、函數、 alias 等設置沒有辦法帶到子Shell里,需要每次啟動非登錄Shell時設置一遍,所以就需要有非登錄Shell的啟動腳本,所以一般來說在 ~/.bash_profile 里設置環境變量,在 ~/.bashrc 里設置本地變量、函數、 alias 等。如果你的Linux帶有圖形系統則不能這樣設置,由于從圖形界面的窗口管理器登錄并不會產生登錄Shell,所以環境變量也應該在 ~/.bashrc 里設置。

4.3.?非交互啟動? 請點評

為執行腳本而 fork 出來的子Shell是非交互Shell,啟動時執行的腳本文件由環境變量 BASH_ENV 定義,相當于自動執行以下命令:

                    if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
                  

如果環境變量 BASH_ENV 的值不是空字符串,則把它的值當作啟動腳本的文件名, source 這個腳本。

4.4.?以sh命令啟動? 請點評

如果以 sh 命令啟動 bash bash 將模擬 sh 的行為,以 ~/.bash_ 開頭的那些啟動腳本就不認了。所以,如果作為交互登錄Shell啟動,或者使用--login參數啟動,則依次執行以下腳本:

  1. /etc/profile

  2. ~/.profile

如果作為交互Shell啟動,相當于自動執行以下命令:

                    if [ -n "$ENV" ]; then . "$ENV"; fi
                  

如果作為非交互Shell啟動,則不執行任何啟動腳本。通常我們寫的Shell腳本都以 #! /bin/sh 開頭,都屬于這種方式。

5.?Shell腳本語法? 請點評

5.1.?條件測試:test [? 請點評

命令 test [ 可以測試一個條件是否成立,如果測試結果為真,則該命令的Exit Status為0,如果測試結果為假,則命令的Exit Status為1(注意與C語言的邏輯表示正好相反)。例如測試兩個數的大小關系:

                        $ VAR=2

$ test $VAR -gt 1

$ echo $?

0

$ test $VAR -gt 3

$ echo $?

1

$ [ $VAR -gt 3 ]

$ echo $?

1
                      

雖然看起來很奇怪,但左方括號 [ 確實是一個命令的名字,傳給命令的各參數之間應該用空格隔開 ,比如, $VAR 、 -gt 、 3 ] [ 命令的四個參數,它們之間必須用空格隔開。命令 test [ 的參數形式是相同的,只不過 test 命令不需要 ] 參數。以 [ 命令為例,常見的測試命令如下表所示:

表?31.2.?測試命令

[ -d DIR ] 如果 DIR 存在并且是一個目錄則為真
[ -f FILE ] 如果 FILE 存在且是一個普通文件則為真
[ -z STRING ] 如果 STRING 的長度為零則為真
[ -n STRING ] 如果 STRING 的長度非零則為真
[ STRING1 = STRING2 ] 如果兩個字符串相同則為真
[ STRING1 != STRING2 ] 如果字符串不相同則為真
[ ARG1 OP ARG2 ] ARG1 ARG2 應該是整數或者取值為整數的變量, OP -eq (等于) -ne (不等于) -lt (小于) -le (小于等于) -gt (大于) -ge (大于等于)之中的一個

和C語言類似,測試條件之間還可以做與、或、非邏輯運算:

表?31.3.?帶與、或、非的測試命令

[ ! EXPR ] EXPR 可以是上表中的任意一種測試條件,!表示邏輯反
[ EXPR1 -a EXPR2 ] EXPR1 EXPR2 可以是上表中的任意一種測試條件, -a 表示邏輯與
[ EXPR1 -o EXPR2 ] EXPR1 EXPR2 可以是上表中的任意一種測試條件, -o 表示邏輯或

例如:

                        $ VAR=abc

$ [ -d Desktop -a $VAR = 'abc' ]

$ echo $?

0
                      

注意,如果上例中的 $VAR 變量事先沒有定義,則被Shell展開為空字符串,會造成測試條件的語法錯誤(展開為 [ -d Desktop -a = 'abc' ] ),作為一種好的Shell編程習慣,應該總是把變量取值放在雙引號之中(展開為 [ -d Desktop -a "" = 'abc' ] ):

                        $ unset VAR

$ [ -d Desktop -a $VAR = 'abc' ]

bash: [: too many arguments

$ [ -d Desktop -a "$VAR" = 'abc' ]

$ echo $?

1
                      

5.2.?if/then/elif/else/fi? 請點評

和C語言類似,在Shell中用 if 、 then elif 、 else 、 fi 這幾條命令實現分支控制。這種流程控制語句本質上也是由若干條Shell命令組成的,例如先前講過的

                        if [ -f ~/.bashrc ]; then

    . ~/.bashrc

fi
                      

其實是三條命令, if [ -f ~/.bashrc ] 是第一條, then . ~/.bashrc 是第二條, fi 是第三條。如果兩條命令寫在同一行則需要用;號隔開,一行只寫一條命令就不需要寫;號了,另外, then 后面有換行,但這條命令沒寫完,Shell會自動續行,把下一行接在 then 后面當作一條命令處理。和 [ 命令一樣,要注意命令和各參數之間必須用空格隔開。 if 命令的參數組成一條子命令,如果該子命令的Exit Status為0(表示真),則執行 then 后面的子命令,如果Exit Status非0(表示假),則執行 elif 、 else 或者 fi 后面的子命令。 if 后面的子命令通常是測試命令,但也可以是其它命令。Shell腳本沒有{}括號,所以用 fi 表示 if 語句塊的結束。見下例:

                        #! /bin/sh



if [ -f /bin/bash ]

then echo "/bin/bash is a file"

else echo "/bin/bash is NOT a file"

fi

if :; then echo "always true"; fi
                      

: 是一個特殊的命令,稱為空命令,該命令不做任何事,但Exit Status總是真。此外,也可以執行 /bin/true /bin/false 得到真或假的Exit Status。再看一個例子:

                        #! /bin/sh



echo "Is it morning? Please answer yes or no."

read YES_OR_NO

if [ "$YES_OR_NO" = "yes" ]; then

  echo "Good morning!"

elif [ "$YES_OR_NO" = "no" ]; then

  echo "Good afternoon!"

else

  echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."

  exit 1

fi

exit 0
                      

上例中的 read 命令的作用是等待用戶輸入一行字符串,將該字符串存到一個Shell變量中。

此外,Shell還提供了&&和||語法,和C語言類似,具有Short-circuit特性,很多Shell腳本喜歡寫成這樣:

                        test "$(whoami)" != 'root' && (echo you are using a non-privileged account; exit 1)
                      

&&相當于“ if...then... ”,而||相當于“ if not...then... ”。&&和||用于連接兩個命令,而上面講的 -a -o 僅用于在測試表達式中連接兩個測試條件,要注意它們的區別,例如,

                        test "$VAR" -gt 1 -a "$VAR" -lt 3
                      

和以下寫法是等價的

                        test "$VAR" -gt 1 && test "$VAR" -lt 3
                      

5.3.?case/esac? 請點評

case 命令可類比C語言的 switch / case 語句, esac 表示 case 語句塊的結束。C語言的 case 只能匹配整型或字符型常量表達式,而Shell腳本的 case 可以匹配字符串和Wildcard,每個匹配分支可以有若干條命令,末尾必須以;;結束,執行時找到第一個匹配的分支并執行相應的命令,然后直接跳到 esac 之后,不需要像C語言一樣用 break 跳出。

                        #! /bin/sh



echo "Is it morning? Please answer yes or no."

read YES_OR_NO

case "$YES_OR_NO" in

yes|y|Yes|YES)

  echo "Good Morning!";;

[nN]*)

  echo "Good Afternoon!";;

*)

  echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."

  exit 1;;

esac

exit 0
                      

使用 case 語句的例子可以在系統服務的腳本目錄 /etc/init.d 中找到。這個目錄下的腳本大多具有這種形式(以 /etc/apache2 為例):

                        case $1 in

	start)

		...

	;;

	stop)

		...

	;;

	reload | force-reload)

		...

	;;

	restart)

	...

	*)

		log_success_msg "Usage: /etc/init.d/apache2 {start|stop|restart|reload|force-reload|start-htcacheclean|stop-htcacheclean}"

		exit 1

	;;

esac
                      

啟動 apache2 服務的命令是

                        $ sudo /etc/init.d/apache2 start
                      

$1 是一個特殊變量,在執行腳本時自動取值為第一個命令行參數,也就是 start ,所以進入 start) 分支執行相關的命令。同理,命令行參數指定為 stop 、 reload restart 可以進入其它分支執行停止服務、重新加載配置文件或重新啟動服務的相關命令。

5.4.?for/do/done? 請點評

Shell腳本的 for 循環結構和C語言很不一樣,它類似于某些編程語言的 foreach 循環。例如:

                        #! /bin/sh



for FRUIT in apple banana pear; do

  echo "I like $FRUIT"

done
                      

FRUIT 是一個循環變量,第一次循環 $FRUIT 的取值是 apple ,第二次取值是 banana ,第三次取值是 pear 。再比如,要將當前目錄下的 chap0 、 chap1 、 chap2 等文件名改為 chap0~ 、 chap1~ chap2~ 等(按慣例,末尾有~字符的文件名表示臨時文件),這個命令可以這樣寫:

                        $ for FILENAME in chap?; do mv $FILENAME $FILENAME~; done
                      

也可以這樣寫:

                        $ for FILENAME in `ls chap?`; do mv $FILENAME $FILENAME~; done
                      

5.5.?while/do/done? 請點評

while 的用法和C語言類似。比如一個驗證密碼的腳本:

                        #! /bin/sh



echo "Enter password:"

read TRY

while [ "$TRY" != "secret" ]; do

  echo "Sorry, try again"

  read TRY

done
                      

下面的例子通過算術運算控制循環的次數:

                        #! /bin/sh



COUNTER=1

while [ "$COUNTER" -lt 10 ]; do

  echo "Here we go again"

  COUNTER=$(($COUNTER+1))

done
                      

Shell還有until循環,類似C語言的do...while循環。本章從略。

1、把上面驗證密碼的程序修改一下,如果用戶輸錯五次密碼就報錯退出。

5.6.?位置參數和特殊變量? 請點評

有很多特殊變量是被Shell自動賦值的,我們已經遇到了 $? $1 ,現在總結一下:

表?31.4.?常用的位置參數和特殊變量

$0 相當于C語言 main 函數的 argv[0]
$1 $2 ... 這些稱為位置參數(Positional Parameter) ,相當于C語言 main 函數的 argv[1] argv[2] ...
$# 相當于C語言 main 函數的 argc - 1 ,注意這里的 # 后面不表示注釋
$@ 表示參數列表 "$1" "$2" ... ,例如可以用在 for 循環中的 in 后面。
$? 上一條命令的Exit Status
$$ 當前Shell的進程號

位置參數可以用 shift 命令左移。比如 shift 3 表示原來的 $4 現在變成 $1 ,原來的 $5 現在變成 $2 等等,原來的 $1 、 $2 $3 丟棄, $0 不移動。不帶參數的 shift 命令相當于 shift 1 。例如:

                        #! /bin/sh



echo "The program $0 is now running"

echo "The first parameter is $1"

echo "The second parameter is $2"

echo "The parameter list is $@"

shift

echo "The first parameter is $1"

echo "The second parameter is $2"

echo "The parameter list is $@"
                      

5.7.?函數? 請點評

和C語言類似,Shell中也有函數的概念,但是函數定義中沒有返回值也沒有參數列表。例如:

                        #! /bin/sh



foo(){ echo "Function foo is called";}

echo "-=start=-"

foo

echo "-=end=-"
                      

注意函數體的左花括號{和后面的命令之間必須有空格或換行,如果將最后一條命令和右花括號 } 寫在同一行,命令末尾必須有;號。

在定義 foo() 函數時并不執行函數體中的命令,就像定義變量一樣,只是給 foo 這個名字一個定義,到后面調用 foo 函數的時候(注意Shell中的函數調用不寫括號)才執行函數體中的命令。Shell腳本中的函數必須先定義后調用,一般把函數定義都寫在腳本的前面,把函數調用和其它命令寫在腳本的最后(類似C語言中的 main 函數,這才是整個腳本實際開始執行命令的地方)。

Shell函數沒有參數列表并不表示不能傳參數,事實上,函數就像是迷你腳本,調用函數時可以傳任意個參數,在函數內同樣是用 $0 、 $1 、 $2 等變量來提取參數,函數中的位置參數相當于函數的局部變量,改變這些變量并不會影響函數外面的 $0 、 $1 、 $2 等變量。函數中可以用 return 命令返回,如果 return 后面跟一個數字則表示函數的Exit Status。

下面這個腳本可以一次創建多個目錄,各目錄名通過命令行參數傳入,腳本逐個測試各目錄是否存在,如果目錄不存在,首先打印信息然后試著創建該目錄。

                        #! /bin/sh



is_directory()

{

  DIR_NAME=$1

  if [ ! -d $DIR_NAME ]; then

    return 1

  else

    return 0

  fi

}



for DIR in "$@"; do

  if is_directory "$DIR"

  then :

  else

    echo "$DIR doesn't exist. Creating it now..."

    mkdir $DIR > /dev/null 2>&1

    if [ $? -ne 0 ]; then

      echo "Cannot create directory $DIR"

      exit 1

    fi

  fi

done
                      

注意 is_directory() 返回0表示真返回1表示假。

Shell腳本


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。。?/p>

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 亚洲一级免费视频 | 国产日本在线播放 | 国产黄色三级 | 国产亚洲精品久久久久久老妇 | a爱片| 天天拍天天干天天操 | 亚洲一区二区三区视频 | 91短视频app下载安装无限看丝瓜山东座 | 国产亚洲一级精品久久 | 日本爽爽爽爽爽爽免费 | 欧美成年性h版影视中文字幕 | 国产精品国产a级 | 亚洲一区国产视频 | 国产精品久久久AV久久久 | 国产99久久精品一区二区永久免费 | 91尤物国产尤物福利 | 99精品99 | 欧美亚洲另类视频 | 国产免费一级高清淫日本片 | 国产综合精品 | 色综合欧美 | 男人与女人做爰毛片A片 | 日韩一级欧美一级毛片在线 | 2021精品国产综合久久 | 天天摸天天碰成人免费视频 | 久久亚洲欧美日本精品品 | 四库影院永久在线精品 | 亚洲图片欧洲电影 | 天天艹夜夜艹 | 日韩欧美在线看 | 国产欧美日韩精品一区二 | 欧美一级视 | a级欧美片免费观看 | 午夜爱爱爱爱爽爽爽网站免费 | 免费视频在线看 | 精品伊人久久久99热这里只 | 国产福利不卡视频在免费 | 欧美成人久久 | 亚洲综合在线另类色区奇米 | 亚洲人成网站在线播放观看 | 亚洲综合精品 |