Unix系列shell程式編寫:上篇

helloxchen發表於2010-10-21
*Shell是什麼?

  任何發明都具有供使用者使用的介面。UNIX供使用者使用的介面就是Shell(DOS的command熟悉吧,但UNIX的要強大的多)。
Shell為使用者提供了輸入命令和引數並可得到命令執行結果的環境。

  為了不同的需要,UNIX提供了不同的Shell。現在的UNIX大部分都支援BourneShell,以下教程就以BourneShell(Bsh)為例,一步步的領略UNIX
Shell的強大功能,佔先其強大魅力,達到更方便靈活的管理、應用UNIX的目的。

  1.UNIX核心和Shell的互動方法

  啟動UNIX時,程式UNIX(核心)將被調入計算機記憶體,並一直保留在記憶體中直到機器關閉。在引導過程中,程式
init將進入後臺執行一直到機器關閉。該程式查詢檔案/etc/inittab,該檔案列出了連線終端的各個埠及其特徵。當發現一個活動的終端時,init程式呼叫getty程式在終端上顯示login等登陸資訊。(username和passwd),在輸入密碼後,
getty呼叫login程式,該程式根據檔案/etc/passwd的內容來驗證使用者的身份。若使用者透過身份驗證,login程式
把使用者的home目錄設定成當前目錄並把控制交給一系列setup程式。setup程式可以是指定的應用程式,通常setup程式
為一個Shell程式,如:/bin/sh 即Bourne Shell(command出來了,呵呵)。

  得到控制後,Shell程式讀取並執行檔案/etc/.profile以及.profile。這兩個檔案分別建立了系統範圍內的和
該使用者自己的工作環境。最後Shell顯示命令提示符,如$。(這是以bsh為例,若是csh,為.cshrc,ksh為.kshrc,bash為.bashrc等等)
   

  注不妨把/etc/.profile和.profile看成DOS的autoexec.bat 或
config.sys檔案)

  當shell退出時,核心把控制交給init程式,該程式重新啟動自動登陸過程。有兩種方法使shell退出,一是使用者執行exit命令,二是
核心(例如root用kill命令)發出一個kill命令結束shell程式。shell退出後,核心回收使用者及程式使用的資源。

  使用者登陸後,使用者命令同計算機互動的關係為:命令程式---&gtShell程式---&gtUNIX核心---&gt計算機硬體。當使用者輸入一個命令,如$ls,
Shell將定位其可執行檔案/bin/ls並把其傳遞給核心執行。核心產生一個新的子程式呼叫並執行/bin/ls。當程式執行完畢後,核心取消
該子程式並把控制交給其父程式,即Shell程式。例如執行:

    $ps

    該命令將會列出使用者正在執行的程式,即Shell程式(下來詳細說說,別急現在)和ps程式。若執行:

    $sleep 10 &
    $ps

  其中第一條命令將產生一個在後臺執行的sleep子程式。ps命令執行時會顯示出該子程式。

  每當使用者執行一條命令時,就會產生一個子程式。該子程式的執行與其父程式或Shell完全無關,這樣可以使Shell去做其他工作。(Shell只是把使用者的意圖告訴核心,然後該幹嘛幹嘛)
現在windows有個計劃任務(在固定的時間,日期自動執行某任務),其實UNIX很早就有這個功能了,也就是所謂的Shell的自動執行。一些UNIX
資源,如cron可以自動執行Shell程式而無需使用者的參與,(這個功能好象在/var/spool/crotab目錄裡)。

Crontab 程式對於系統管理員來說是非常有用的。Cron
服務用於計劃程式在特定時間(月、日、周、時、分)執行。我們以root的crontab 為例。根使用者的
crontab 檔案放在 /var/spool/crontab/root 中,其格式如下:

  (1)  (2)  (3)  (4)  (5)  (6)
   0   0   *   *   3   /usr/bin/updatedb
      1. 分鐘 (0-60)
      2. 小時 (0-23)
      3. 日 (1-31)
      4. 月 (1-12)
      5. 星期 (1-7)
      6. 所要執行的程式
  2.Shell的功能和特點
  1>命令列解釋
  2>使用保留字
  3>使用Shell元字元(萬用字元)
  4>可處理程式命令
  5>使用輸入輸出重定向和管道
  6>維護一些變數
  7>執行環境控制
  8>支援Shell程式設計

  對於"命令列解釋"就不多說了,就是在shell提示符(例如:"$","%","#"等)後輸入一行unix命令,Shell將接收使用者的輸入。

  "使用保留字":Shell有一些具有特殊意義的字,例如在Shell指令碼中,do,done,for等字用來控制迴圈操作,if,then等控制條件操作。
保留字隨Shell環境的不同而不同。

  "萬用字元":* 匹配任何位置
       ? 匹配單個字元
       [] 匹配的字元範圍或列表 例如:
       
          $ls [a-c]*
         
          將列出以a-c範圍內字元開頭的所有檔案
          $ls [a,m,t]*
         將列出以e,m或t開頭的所有檔案

  "程式命令"
:當使用者輸入命令後,Shell讀取環境變數$path(一般在使用者自己的.profile中設定),該變數包含了命令可執行檔案可能存在的目錄列表。
shell從這些目錄中尋找命令所對應的可執行檔案,然後將該檔案送給核心執行。

  "輸入輸出重定向及管道" :重定向的功能同DOS的重定向功能:

     ">" 重定向輸出
     "
  而管道符號,是unix功能強大的一個地方,符號是一條豎線:"|",用法:
command 1 | command 2 他的功能是把第一個命令command 1執行的結果作為command
2的輸入傳給command 2,例如:

    $ls -s|sort -nr|pg

  該命令列出當前目錄中的所有檔案,並把輸出送給sort命令作為輸入,sort命令按數字遞減的順序把ls的輸出排序。然後把排序後的
內容傳送給pg命令,pg命令在顯示器上顯示sort命令排序後的內容。

  "維護變數"
:Shell可以維護一些變數。變數中存放一些資料供以後使用。使用者可以用"="給變數賦值,如:

         $lookup=/usr/mydir

該命令建立一個名為lookup的變數並給其賦值/usr/mydir,以後使用者可以在命令列中使用lookup來代替/usr/mydir,例如:
         
         $echo $lookup
         結果顯示:/usr/mydir

         為了使變數能被子程式使用,可用exprot命令,例如:

         $lookup=/usr/mydir
         $export lookup

  "執行環境控制"
:當使用者登陸啟動shell後,shell要為使用者建立一個工作的環境,如下:

  1>當login程式啟用使用者shell後,將為使用者建立環境變數。從/etc/profile和.profile檔案中讀出,在這些檔案中一般都用$TERM
變數設定終端型別,用$PATH變數設定Shell尋找可執行檔案的路徑。

  2>從/etc/passwd檔案或命令列啟動shell時,使用者可以給shell程式指定一些引數,例如"-x",可以在命令執行前顯示該命令及其引數。後面詳細介紹這些引數。

  "shell程式設計" :本文主要介紹的內容。

  shell本身也是一種語言(*可以先理解為unix命令的組合,加上類C的條件,迴圈等程式控制語句,類似dos批處理,但要強大的多),使用者可以
透過shell程式設計(指令碼,文字檔案),完成特定的工作。

SHELL變數

  下面我們詳細的介紹Bourne Shell的程式設計:

  自從貝爾實驗室設計了Bourne
Shell。從那時起許多廠商根據不同的硬體平臺設計了許多版本得unix。但在眾多版本的unix中,Bourne Shell
一直保持一致。
  1>Bsh的啟動:使用者在登陸後,系統根據檔案/etc/passwd中有關該使用者的資訊項啟動Shell。例如某使用者在passwd中
的資訊項為:

    ice_walk:!:411:103:Imsnow ,ice_walk:/home/ice_walk:/bin/bsh

  則表明,使用者名稱是ice_walk等資訊,在最後一項"/bin/bsh"表明使用者的sh環境型別是bsh,於是系統啟動之。在啟動或執行(包括下面我們要講
的shell程式--指令碼)過程中可以使用以下一些引數,我們一一說明:

  -a 將所有變數輸出
  -c "string"從string中讀取命令
  -e 使用非互動式模式
  -f 禁止shell檔名產生
  -h 定義
  -i 互動式模式
  -k 為命令的執行設定選項
  -n 讀取命令但不執行
  -r 受限模式
  -s 命令從標準輸入讀取
  -t 執行一命令,然後退出shell
  -u 在替換時,使用未設定的變數將會出錯
  -v 顯示shell的輸入行
  -x 跟蹤模式,顯示執行的命令

許多模式可以組合起來用,您可以試試了,但-ei好象不行,你說why呢?

  使用set可以設定或取消shell的選項來改變shell環境。開啟選項用"-",關閉選項用"+",多數unix允許開啟或關閉a、f、e、h、k、n、
u、v和x選項。若顯示Shell中已經設定的選項,執行:

    $echo $-

  Bsh中每個使用者的home目錄下都有一個.profile檔案,可以修改該檔案來修改shell環境。為了增加一個可執行檔案的路徑(例如/ice_walk/bin),
可以把下面程式碼加入.profile中

    PATH=$PATH:/ice_walk/bin;exprot PATH

   .profile中shell的環境變數意思如下:

    CDPATH 執行cd命令時使用的搜尋路徑
    HOME 使用者的home目錄
    IFS 內部的域分割符,一般為空格符、製表符、或換行符
    MAIL 指定特定檔案(信箱)的路徑,有UNIX郵件系統使用
    PATH 尋找命令的搜尋路徑(同dos的config.sys的 path)
    PS1 主命令提示符,預設是"$"
    PS2 從命令提示符,預設是">"
    TERM 使用終端型別

  2>Bsh裡特殊字元及其含義

  在Bsh中有一組非字母字元。這些字元的用途分為四類:作為特殊變數名、產生檔名、資料或程式控制以及引用和逃逸字元控制。他們
可以讓使用者在Shell中使用最少的程式碼完成複雜的任務。

     *> Shell變數名使用的特殊字元
        $# 傳送給命令Shell的引數序號
        $- 在Shell啟動或使用set命令時提供選項
        $? 上一條命令執行後返回的值
        $$ 當前shell的程式號
        $! 上一個子程式的程式號
        $@ 所有的引數,每個都用雙括號括起
        $* 所有引數,用雙括號括起
        $n 位置引數值,n表示位置
        $0 當前shell名
     *>產生檔名的特殊字元
        包括"*","?","[]",上面講過,不再多說。
     *>資料或程式控制使用的特殊字元
        >(file) 輸出重定向到檔案中(沒有檔案則建立,有則覆蓋)
        >>(file)
輸出重定向到檔案中(沒有則建立,有則追加到檔案尾部)
                ; 命令分割符
        | 管道符
        & 後臺執行(例如:sleep 10 &)
        ` ` 命令替換,重定向一條命令的輸出作為另一命令的引數
     *>對於引用或逃逸的特殊字元

Bsh用單引號' '和雙引號"
"將特殊字元或由空白分隔的字引用起來組成一個簡單的資料串.使用單引號和雙引號的區別是雙引號中的內容可進行引數和變數替換.逃逸字元也一樣.

        $echo "$HOME $PATH"
         結果顯示$/u/ice_walk/bin:/etc:/usr/bin
        而$echo '$HOME $PATH' 結果顯示$HOME $PATH

  shell的逃逸符是一個"",表示其後的字元不具有特殊的含義或不是shell的函式

        $echo $HOME $PATH
        結果顯$$HOME /bin:/etc:/usr/bin:

3>Bsh的變數

  前面我們在多個地方引用了變數,當Shell遇到一個"$"符時(沒有被引用或逃逸),它將認為其後為一變數。不論該變數是環境變數還是使用者自定義的變數,在命令列中變數名要被變數值替換。例如命令:ls
$HOME將列出變數HOME對應目錄下的檔案。
使用者可以在命令列中的任何地方進行變數替換。包括命令名本身,例如:

    $dir=ls
    $$dir f*

  將列出以f開頭的檔案。

  現在詳細的介紹下Bsh的變數。Bsh中有四類變數:使用者定義的變數、位置變數(shell引數)、預定義變數及環境變數。

  使用者定義的變數:

  使用者定義的變數由字母和下劃線組成,並且變數名的第一個字元不能為數字(0~9)。與其他UNIX名字一樣,變數名是大小寫敏感的。使用者可以在命令列上用"="給變數賦值,例如:

    $NAME=ice_walk

  給變數NAME賦值為ice_walk,在應用變數NAME的時候,在NAME前加"$"即可,前面已說,不再廢話(別說我廢話多,關鍵是沒當過老師)。可以用變數和其他字元組成新的字,例如:

    $SUN=sun
    $echo ${SUN}day

  在應用shell變數時候,可以在變數名字兩邊$後面加上{},以更加清楚的顯示給shell,哪個是真正的變數,以實現字串的合併等功能。
 
  結果顯示:sunday(注意不能echo
$SUNday,因為SUNday變數沒定義,讀者試下執行結果)
使用者也可以在命令列上同時對多個變數賦值,賦值語句之間用空格分開:

    $X=x Y=y

    注意變數賦值是從右到左進行的

    $X=$Y Y=y
    X的值是y
    $X=z Y=$Z

    Y的值是空(變數未賦值時,shell不報錯,而是賦值為空)

  使用者可以使用"unset "命令清除給變數賦的值

使用者使用變數時要在其前面加一"$"符,使變數名被變數值所替換。Bsh可以進行變數的條件替換,即只有某種條件發生時才進行替換。替換條件放在一對大括號{}中,如:

    ${variable: -value}
variable是一變數值,value是變數替換使用的預設值

    $echo Hello $UNAME
    結果顯示:Hello
    $echo Hello ${UNAME: -there}
    結果顯示:Hello there
    $echo $UNAME
    結果顯示: (空)
    $UNAME=John
    $echo Hello ${UNAME: -there}
    結果顯示:Hello John

  可以看出,變數替換時將使用命令列中定義的預設值,但變數的值並沒有因此而改變。另外一種替換的方法是不但使用預設值進行替換,而且將預設值賦給該變數。其形式如下:

    ${variable:=value}

  該形式在變數替換後同時把值value符給變數variable。

    $echo Hello $UNAME
    結果顯示:Hello
    $echo Hello ${UNAME:=there}
    結果顯示:Hello there
    $echo $UNAME
    結果顯示:there
    $UNAME=John
    $echo Hello ${UNAME:-there}
    結果顯示:Hello John

  變數替換的值也可以是` `括起來的命令:

    $USERDIR={$Mydir: -`pwd`}

  第三種變數的替換方法是隻有當變數已賦值時才用指定值替換形式:

    ${variable: +value}

    只有變數variable已賦值時,其值才用value替換,否則不進行任何替換,例如:

    $ERROPT=A
    $echo ${ERROPT: +"Error tracking is acitive"}
    結果顯示:Error tracking is acitive
    $ERROPT=
    $echo ${ERROPT: +"Error tracking is acitive"}
    結果顯示: (空)

  我們還可以使用錯誤檢查的條件進行變數替換:

    ${variable:?message}

當變數variable已設定時,正常替換。否則訊息message將送到標準錯誤輸出(若此替換出現在shell程式中,那麼該程式將終止)。 例如:

    $UNAME=
    $echo $ {UNAME:?"UNAME HAS NOT BEEN SET"}
    結果顯示:UNAME HAS NOT BEEN SET

    $UNAME=Stephanie
    $echo $ {UNAME:?"UNAME HAS NOT BEEN SET"}

    結果顯示:Stephanie
    當沒有指定message時,shell將顯示一條預設的訊息,例如:

    $UNAME=
    $echo $ {UNAME:?}
    結果顯示:sh:UNAME:parameter null or not set

4>位置變數或Shell引數

  在shell解釋使用者的命令時,將把命令列的第一個字作為命令,而其他的字作為引數。當命令對應的可執行檔案為Shell程式時,這些引數將作為位置變數傳送給該程式。第一個引數記為$1,第二個為$2....第九個為$9。其中1到9是真正的引數名,"$"符只是用來標識變數的替換。

  位置變數$0指命令對應的可執行檔名。在後面將詳細介紹位置變數。

  1.只讀變數

  使用者將變數賦值後,為了防止以後對該變數的修改,可以用以下命令將該變數設定為只讀變數:

    readonly variable

  2.export命令

  shell執行一個程式時,首先為該程式建立一個新的執行環境,稱為子shell。在Bourne
Shell中變數都是區域性的,即他們只在建立他們的Shell中有意義。使用者可以用export命令讓變數被其他子Shell識別。但某使用者的變數是沒法讓其他使用者使用的。

  當使用者啟動一個新shell時,該shell將使用預設的提示符。因為賦給變數PS1的值只在當前shell中有效。為了讓子Shell使用當前Shell中定義的提示符號,可以使用export命令:

    $PS1="Enter command:"
    Enter command:export PS1
    Enter command:sh
    Enter command:

    此時變數PS1變成了全域性變數。它可以被其子Shell使用。當變數被設定成全域性的以後,將一直保持有效直到使用者退出該變數所在的Shell。使用者可以在檔案.profile中給一個變數永久賦值。詳見"規範Shell"。

基本語句

  從本節起,我們將詳細介紹Shell程式設計的基本知識,透過編寫Shell指令碼,使用者可以根據自己的需要有條件的或者重複的執行命令。透過Shell程式,可以把單個的UNIX命令組合成一個完全實用的工具,完成使用者的任務。

  1>什麼是Shell程式

  當使用者在UNIX Shell中輸入了一條複雜的命令,如:

    $ls -R /|greo myname |pg

  我們可以稱使用者在對Shell程式設計,當把這條語句寫在一個檔案裡,並且符給該檔案可執行許可權,那麼該檔案就是我們傳統上說的Shell程式。

  2>簡單的Shell程式

  假設使用者每天使用下述命令備份自己的資料檔案:

    $cd /usr/icewalk;ls * |cpio -o > /dev/fd0

  我們可以把它寫在一個檔案,如:ba.sh中:

    $cat >ba.sh
    cd /usr/icewalk
    ls * |cpio -o > /dev/fd0
    ^D  (ctrl_d)

  程式ba.sh就是Shell指令碼,使用者可以用vi或其他編輯工具編寫更復雜的指令碼。

  此時使用者備份檔案只需要執行Shell程式ba.sh,執行時需在當前Shell中建立一個子Shell:

    $sh ba.sh

  程式sh與使用者登陸時執行的Bourne
Shell相同,但當Sh命令帶引數ba.sh後,它將不再是一個互動式的Shell,而是直接從檔案ba.sh中讀取命令。

  執行ba.sh中命令的另一方法是給檔案ba.sh執行許可權:

    $chmod +x ba.sh

  此時,使用者可以輸入檔名ba.sh做為一個命令來備份自己的資料,需要注意的是,用這種方法執行命令的時候,檔案ba.sh必須存在於環境變數$PATH所指定的路徑上。
[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24790158/viewspace-1040125/,如需轉載,請註明出處,否則將追究法律責任。

相關文章