·ChinaUnix首页 ·论坛 ·博客 
Linux首页 | Linux新闻 | Linux论坛 | Linux文档 | Linux下载 | Linux博客 | Linux搜索 | 开源项目孵化平台 | 《开源时代》
新手入门 | 安装启动 | 管理员指南 | 开发手册 | 桌面应用 | 程序开发 | 数据库 | 网络技术| CentOS | Fedora | MySQL | Apache | Ubuntu | Gentoo| OSCON08
  Linux时代 >> 技术文档 >> 程序开发
 
UNIX Shell Programming
来源: ChinaUnix博客  日期: 2007.01.12 13:32 (共有条评论) 我要评论
 

http://www.freebsd.org.hk/html/other/shell1.html

Chap 0 -- 前言 ( UNIX Shell Programming not for guru ...... )
(0.1) 讀者需要那些基礎 ?
這篇文章的主題是 UNIX Shell Programming . 閱讀這篇文章須要一點
基礎 , 包括 : 對 UNIX 的命令要有一點認識 , 尤其是常用 , 重要的
命令 . 此外 , 你也必需會在 UNIX 上使用 editor , 像 vi , joe 或
emacs 等 ...... 當然 , 假如你有寫程式的經驗 , 那麼 UNIX Shell
Programming 對你來說不過是反掌折枝罷了 .
(0.2) 使用那一種 Shell ? Why ?
既然 Shell 有很多種 , 我們所要介紹是最早由 AT&T Bell Labs 的
Stephen Bourne 所寫的標準 Shell , 也就是所謂的 Bourne Shell .
在一些情況下 , 也會順便提一下 C Shell 相關的東西 .
既然現在已經有了更新更好的 Shell , 那我們為什麼還要介紹 Bourne
Shell 呢? 這是因為 , 在 UNIX Shell Programming 中 , 有許多都
還是用 Bourne Shell 的語法寫成的 , 尤其像 /etc/rc.X ( SunOS )
而一般使用者或許用 C Shell 的增強版 tcsh 或 bash ( Bourne Again
Shell ) 這些比較新的 Shell . 不過沒有關係 , 只要系統中有 sh
( Bourne Shell ) 這個直譯器存在 , 那麼 , 我們都可以用 Bourne
Shell 的語法來寫程式 . ( 沒有 sh 的系統 , 是很難想像的 ...... )
還有一點 , 用 sh 來寫程式實際上比用 csh 要好 , 網路上已經有
討論過了 , 很多書也都提到用 csh 寫程式的缺點 , 並建議不要用
csh 來寫程式 . 我個人沒有什麼意見 , sh 與 csh 就好比程式語言
中的 Pascal 與 C (只是比喻) , 想必學過這兩種語言的人都知道 ,
只要學會了其中一種 , 另一種也不算太難學 , sh 與 csh 也是一樣 .
況且寫程式能真正作事就可以 , 不需要一下子用 sh , 一下子用 csh
來寫程式 .
*************************************************************
注意 !!!! 所有的例子都要在 Bourne Shell 系列的環境下操作
建議先看看 2.3 , 才不會搞了半天 , 才發覺原來你是用 C Shell
系列的 Shell (Shell 的種類及系列可參考 2.3)
此外 , 文中的一些例子都是在 Linux 下操作 , 在 SunOS 或 HP
上 , 命令也許有少許的不同 . 當你在演練時 , 假如不能正確的
執行 , 你應該看看指令的 manual . 像 SunOS 的 find 與 Linux
的 find 就有差異 . 大部份的例子都可以在各種不同的平臺上執行 .
*************************************************************
(0.3) 本篇文章的架構
本來想用 html 來寫的 , 這樣似乎比較好 , 但是覺得有點麻煩 , 所
以還是用平常的格式來寫 , 各位假如用 joe , 那麼當看到 (參考 x.y)
時 , 你還是可以用 ctrl-k f 來尋找 x.y 的標題 ; 在 vi 下也可以用
/x.y 來尋找 , 這樣也許也可以達到 hypertext 的效果吧 ...... :)
Chap 1 -- 簡介
Chap 2 -- 基本知識
Chap 3 -- Shell Programming 中的資料表示
Chap 4 -- Shell Programming 中的語法
Chap 5 -- 讀取資料及程式偵錯
附錄一 -- Shell Summary 及 一些 Shell Script 的例子
附錄二 -- grep 簡介及 regular expressions
參考資料 -- 介紹一些相關的書及資料
(0.4) 假如你已經對 Bourne Shell Programming 很熟 ......
那也許附錄一 Shell Summary 會對你有複習及快速參考的好處 !
還有 , 參考資料中的書目 , 都是值得一看的書 , 假如你還沒看過 ,
那我想你可以參考看看 . 當然 , man 及 grep 的配合 , 也常常
可以助你一臂之力 .
Chap 1 -- 簡介
無疑的 , UNIX 是一個功能強大的作業系統 , 它多人多工特性 , 網路
的支援 , 多采多姿的 X Windows System , 都相當的吸引人 . 除此之外
UNIX 還有一項重要的特性 , 就是它提供了數以百計的命令 .
每個人都知道 , DOS 下面也不過二三十個命令 , 這二三十個命令可能
一兩天就可以學的很好了 ; 但 UNIX 作業系統則不然 , 數百個命令就算
都學會了 , 若我們不知道如何去 "組合這些命令" , 那我們將失去學習
UNIX 的最大樂趣 !!
有人會覺得疑惑 : "組合命令"會有趣嗎 ? 什麼叫作 "組合命令" ?
這就是我們所要學習的 . 現在就舉一個最簡單的例子而言 : 請計算目前
工作目錄下共有多少個普通檔案 ( Regular File ) ?
當你遇到這樣的問題 , 你要如何解決 ? 有人會說 : " 我用 ls -l 列
出目前工作目錄下所有的檔案 , 然後慢慢的數 ...... " , 假如你用了
這樣的方法 , 而目前工作目錄下又有數百個檔案 , 那麼 , 我真的很敬
佩你 "愚公移山" 的精神 . 還有些人比較 "暴力" , 他說 : " 這個嘛
很簡單 , 我寫一個 C 語言程式 , 用 opendir , lstat 等函數來計算
某一目錄下有多少個普通檔案 ...... " 假如你要這樣做 , 那我當然不
反對 , 常練習寫程式也是不錯的 . 然而 , 就為了這麼小小的一個問題
而寫一段 C 語言的程式 , 未免太小題大作 . 實際上 , 解答的關鍵就
在 : " 你是不是會從數百個 UNIX 的命令中挑出適合的命令 , 然後把
這些命令組合起來 , 以達到我們所要的目的 !! " . " UNIX 的每個命令
就好比各種不同的 IC , 每種 IC 都有它特殊的作用 , 藉由不同 IC 的
組合 . 我們可以達到我們所要的功能 " , " 要達到目的的方法 , 常常
不是只有一種 ......" 上面這幾句話相當重要 , 各位現在也許還不清
楚 . 不過上面幾句話所提的 , 的確就是 UNIX 頗為吸引人的地方之一 .
除了命令之外 , Shell 提供了一些類似一般程式語言的語法 , 我們可
以利用這些語法來寫程式 . 而這點也正是本篇文章的最主要內容 .
Chap 2 -- 基本知識
(2.1) 什麼是 Shell ?
我們可以把 UNIX 切成兩方面來看 , 一部份是系統的核心 (kernel) ,
另一部份就是系統中的應用程式 . 舉凡我們常用的一些命令 , 像
ls , find 與 Shell 一樣 , 都可被歸類為應用程式 . 所以 , Shell
的地位與其它的應用程式並沒有什麼差別 .
我們可以看看簽入 UNIX 作業系統的過程 : 當我們輸入 username 之
後 , getty 就結束 , 取而代之的是 login . 在我們輸入正確的
password 後 , 系統就依照 /etc/passwd 中的記錄 , 為使用者啟動
相對應的 Shell . 在這個時候 , 使用者就會得到一個提示符號 .
接著我們就可以下達一些命令 .
--  getty --> login --> shell --> logout --
^                                          |
|------------------------------------------
Shell 有另一個名稱叫做 "命令列直譯器" (command interpreter)
從這個名稱當中 , 我們就可以了解到 Shell 實際上為命令列的翻譯官
當我們在提示符號後輸入一長串的命令 , 並按下 Enter 之後 , Shell
就將我們所輸入的東西 , 做一個適當的分析及處理 .
(2.2) Shell 負責的事
對系統做交談性的使用 . 包含了輸出輸入導向 ( 參考 2.5 ) ,
管線 ( 參考 2.6 ) Wildcard 的展開及匹配 ( 參考 2.4 ) , 另外
還有 Job Control( 從 SVR4 開始) , 變數環境設定?. 解譯命令 ....
還有一點很重要 , Shell 有它自己內建的程式語言 , 利用它的語法
我們可以來寫程式 , 寫出來程式功能並不像一般的程式語言 ( 如 C
語言 ) 那麼多 , 然而 , 在很多情況下就夠用了 . 這部份也就是整
篇文章的重點 .
(2.3) UNIX 系統上 Shell 的種類
在 DOS 中 , 想必各位一定都知道它的 Shell 為 command.com . 特別
要說明的是 : " Shell 是可以被置換掉的 ! " 所以 , 許多人在 DOS
中 , 常常把他們的 Shell 由 command.com 換成 4dos.com , 以得到
更多的功能 , 這已經是很平常的事了 . 在 UNIX 系統中也是一樣 ,
使用 chsh 這個命令 , 你可以挑你喜歡的 Shell 來使用 . 合法的
Shell 清單被列在 /etc/shells 這個檔案中 .
Shell 的種類可以分為兩個支派 , 一為由 Bourne Shell 衍生而來的
包括了 sh (Bourne Shell) , ksh (Korn Shell) , bash (Bourne
Again Shell) , zsh (Z Shell) ; 另一支派為 C Shell 衍生而來的 ,
包括了 csh , tcsh . 現在許多人作業的環境常常是在 csh 或 tcsh
之下 , 尤其是從 BSD 衍生而來 SunOS 的環境 .
你可以使用 echo $SHELL 來獲知你現在到底使用那一種 Shell . 或
者直接看 /etc/passwd 的最後一個欄位 .
(2.4) Wildcard 的展開及匹配
      Wildcard 有些人把它翻譯成 "萬用字元" , 下面所列舉的
      項目 , 並不盡然每一種 Shell 都支援 , 各位要自己試試看 .
(2.4.1) * : 代表任意的字串 ( 字串可以是空的 )
            % ls
            haha    haha1   haha2   memo1   memo13  memo2   memo23
            % ls haha*
            haha   haha1  haha2
            % ls memo*
            memo1   memo13  memo2   memo23
(2.4.2) ? : 代表任意一個字元
            % ls
            memo1   memo13  memo2   memo23
            % ls memo?
            memo1  memo2
            % ls memo1?
            memo13
(2.4.3) []: 代表符合[]中所列舉的字元
            % ls
            haha    haha1   haha2   memo1   memo13  memo2   memo23
            % ls memo[12]
            memo1  memo2
            [a-e]bc 會符合 abc , bbc , cbc , dbc , ebc   
              
(2.4.4) [!]: [] 中的字元 , 若加了 ! , 則表示不符合[]中所列舉的字元
            % ls
            haha    haha1   haha2   memo1   memo13  memo2   memo23
            % ls memo[!1]
            memo2
            [!a-z]bc 會符合不是以小寫英文字母開頭 , 後面接著 bc
            的字串 . 如會符合 : Cbc , 6bc 等 ......   
(2.4.5) {word1,word2....} : 如 my_{dog,cat,pig} 會符合 my_dog
         my_cat , my_pig
   
(2.4.6) \ : 接在 \ 之後的特殊字元 , 其特殊的意義會被取消
            例如 , 有一個檔案 , 它的名字真的叫 *abc , 那我們得用
            cat \*abc 才能把 *abc 中的內容顯示出來 .

(2.4.7) 更多關於 Wildcard 的討論 :
   (I)  glob : 這種 Wildcard 匹配的動作 , 我們稱為 " globbing "
        在很多種 Shell 之中都有一個叫作 noglob 的變數 , 可以把
        Wildcard 展開匹配的動作關掉 .   
        % ls *bc
        abc  bbc  cbc  dbc
               
        % set noglob    output_file
這個 > 的符號就是導向符號 .
下表是一般經常看到的輸出輸入導向 , prog 就是 program 的意思
而 file 就是一個檔案:
      功能                    sh,ksh,bash         csh,tcsh
--------------------------------------------------------------
把標準輸出導到一個檔案        prog > file         prog > file
把標準輸出導到檔案描述值 n    prog >&n
把原來輸出至檔案描述值 m      prog m>&n
的輸出與輸出至檔案描述值 n
的輸出 , 一起導向至檔案描
述值 n
把標準錯誤輸出導到一個檔案    prog 2> file
把標準輸出關閉                prog >&-
把標準輸出以及標準錯誤輸出    prog > file 2>&1    prog >& file
導到一個檔案
把標準輸出導到 f1 , 把標準   (prog > f1) 2>f2   (prog > f1) >& f2
錯誤輸出導到 f2
--------------------------------------------------------------
把標準輸出導到一個檔案的後面    prog >> file        prog >> file
把標準錯誤輸出導到一個檔案的    prog 2>> file
後面
把標準輸出以及標準錯誤輸出導    prog >> file 2>&1   prog >>& file
到一個檔案的後面
--------------------------------------------------------------
把檔案當作標準輸入            prog  file2
輸入 , 並把標準輸出的結果
再導向至 file2
--------------------------------------------------------------
從鍵盤的輸入當作 stdin , 直     prog  file
    w > who_log
    把 w 這個指令的輸出導到一個叫做 who_log 的檔案
    cat file1 > file2
    ^^^^^^^^^
    把 cat file1 的輸出導到一個叫做 file2 的檔案
(II) prog 2> file
     先寫一個叫做 test1.c 的 C 語言程式如下 :
     #include
     void main(void)
     {
       print("Standard Error Test");   /* 故意寫錯 */
     }
     接著輸入  cc test1.c               errmsg
     此時 , 原本在終端機上的錯誤輸出就會被導到 errmsg 這個檔案中
   
     還有 , prog 2> /dev/null 可以抑制錯誤訊息的輸出 , 因為它把
     標準錯誤輸出導到 /dev/null 這個垃圾桶中 , 這個功能與上面的
     prog >&- 有相同的地方 , 本來應該輸出的訊息都被壓抑了 . 只
     不過一個是抑制錯誤訊息輸出 , 一個是抑制正常訊息輸出 . 不過
     prog 2>&- 就可以達到與 prog 2> /dev/null 相同的效果 .
(III) prog > file 2>&1
     我們可以試試下面的命令 :
     find / -size +2000k > ~/output 2>&1
     接著你可以在你的 Home Directory 下找到 output 這個檔案 , 你
     可以看看它的內容 . 你就會發覺 , 有些目錄對你來說是
     Permission denied 的 , 這些都是錯誤訊息的輸出 . 你也會看到
     有些檔案名 , 這些檔案都是大於 2000k 的 , 屬於正常訊息的輸出
     所以 , output 這個檔案的確包含著標準輸出以及標準錯誤輸出的
     訊息 .
     ** 注意 !!! SunOS 上的 find 是以 block 為單位 , 所以 , 假
     如你在 SunOS 上操作 , 那請把 +2000k 的 k 去掉 , 如下 :
     find / -size +2000 > ~/output 2>&1
     
(IV) (prog > f1) 2>f2
     再一次的 , 我們用上面的命令 , 但改為 :
     (find / -size +2000k > ~/normal_out) 2>/tmp/error_out
     在 (III) 中 , 我們把正常訊息的輸出與錯誤訊息的輸出都混在
     ~/output 這個檔案中 . 從某個角度來說 , 這不是很好的作法 ,
     我們所希望的 , 是把正常訊息的輸出放在一個檔案 , 而錯誤
     訊息的輸出放到另一個檔案 . 此時 , 我們就要採用 (IV) 這個
     方法 . 在上面的命令中 , 我們把 find 這個命令的正常訊息輸出
     導到 ~/normal_out 這個檔案 , 而錯誤訊息輸出我們就把它導到
     /tmp/error_out 這個檔案中 .
      
(V) 至於 prog >> file  ,  prog 2>> file  ,  prog >> file 2>&1
    與 prog > file  ,  prog 2> file  ,  prog > file 2>&1
    只是把 > 換成 >>  , 而它的差別就只在 :  >> 是"附加"的意思
    並不像 > 會把原來導向過去的檔案清除 (假如欲導向過去的檔案
    已經存在 , 而且 noclobber 變數沒有被設定的話 !當然 , 若原來
    檔案不存在 , 那就會製造出新的檔案)
    所以 , 試試看下面的命令就可明白 :
    ls -la /usr > file1
    ls -la /etc > file1
    cat file1        file2
    ls -la /etc >> file2   file2
    這個型式是由兩個導向符號所組成 , 看起來有點複雜 , 其實一點也
    不 . 只要你拆成兩部份來看 : prog  file2
                                ^^^^^^^^^^^^            (i)
                                ^^^^^^^^^^^^^^^^^^^^    (ii)
    第 (i) 部份 : 把 file1 當作是 prog 的標準輸入
    第 (ii) 部份 : 把第 (i) 部份的輸出結果 , 導向至 file2 中
    舉個例子 :
    tr 'a-z' 'A-Z'  dest
    首先 , tr 'a-z' 'A-Z'  dest 的話 , 我們會在
    終端機上看到轉換後的輸出 . 然而 , 我們使用 > dest 再把原來應
    該在終端機上的輸出導向到 dest 這個檔案中 . 所以 , dest 就會
    是 sour 經過轉換後的內容 .
(VIII) prog  this is a test
    > but ....
    > End_Of_Letter              This is Here Document Test
    > This Sample is quite typical
    > You will see the same style in some Shell Script
    > See You
    > It_is_OK
    如此作可以一次顯示一大段訊息 , 而不必很麻煩的用許多列 echo            
    來做 .
(2.6) 管線 (Pipes)
管線就是達成 "組合命令" 的方法 , 極端重要 ! 它的型式是這種樣子
  prog1 | prog2
其中 , | 就是管線的符號 , 上面的意思是 : 把 prog1 的輸出 , 當
做 prog2 的輸入 . 你也可以想像成 : prog1 及 prog2 是兩臺機器 ,
| 是它們之間的傳送帶 , 經過 prog1 處理過的東西 , 再交給 prog2
去加工 . 舉幾個例子 :
  ls -la | more    [1-5]) echo 'haha';;
> [6-10]) echo 'lala';;
> esac
haha
你自己也可以試試看 , 不過你得使用 Bourne Shell 系列的 Shell
好了 , 我們已經看到可以在提示符號下寫程式 , 然而 , 我們並不
喜歡這樣做 . 因為在提示符號下要修改程式很麻煩 , 小的程式也就
算了 , 大的程式我想沒有人會喜歡這種方法 . 所以 , 在一般的情
況下 , 我們可以先用文書編輯器寫好一個程式並存成一個檔案 , 然
後再把這個檔案交給 Shell 去執行 , 而這個檔案就稱為 Shell
Script ! 先給各位看看一個最簡單的 Shell Script :
  
  #!/bin/sh
  ps -a | more
上面的兩列內容 , 我把它放在一個檔案中 , 然後直接執行它就可以.
但不要忘記 , 你要對這個檔案有讀以及執行的權限 .
(3.2) Shell Programming 的兩大要素 : 資料及語法  
假如你學過高階語言 , 你應該可以了解到 , 其實每種高階語言的概
念都差不多 , 它們只是語法不同 , 也許資料表示的方法也有少許的
差別 . 但是 , 學會了一種再去學別種並不算難 .
Shell Programming 也是一樣 , 它還是有大家熟悉的語法如 if , do
for , while 等等的東西 . 比較特殊的是它的資料型態 .
下面就開始進入 Shell Programming 的資料表示 . 語法請看 Chap 4
(3.3) 註解及 #!/bin/sh
很多程式語言都有其註解格式 , 在一個 Shell Script 中 , 假如我
們看到一個 # 號 , 那從 # 號開始一直到那列的結尾都是註解 .
註解只是提高程式的可閱讀度 , 對 Shell 來說並不會做解譯的動作
舉一個 Shell Script 如下:
#!/bin/sh
#  
# Count_bash_user_number : report how many users use bash
#
grep -c '/bin/bash' /etc/passwd    # grep -c : print match
                                   # number
這個 Shell Script 中 , 真正作事的只有一列 , 其它 # 後接的文
字都是註解 . 但特別要注意的是 : #!/bin/sh 不是註解 , 雖然它
是以 # 為開頭 , 但是它有特殊的意義 . 在最早的時候因為只有 sh
所以也並不需要 #!/bin/sh , 但是後來 Shell 的種類越來越多 ,
有些人用 csh , 有些人用 tcsh . 這時候我們就要特別的指出 , 這
個 Shell Script 是用那一種 Shell 寫的 . 假如你的 Shell Script
是以 Borune Shell 的語法寫成的 , 那你就得加上 #!/bin/sh , 假
如是以 C Shell 的語法寫成的 , 那你得加上 #!/bin/csh . 這列一
定要加嗎 ? 答案是不盡然 , 如果你目前使用的 Shell 與你寫的
Shell Script 使用相同的語法 , 那可以不加 . 但是我舉一個例子:
假如你用的是 tcsh , 但卻用 sh 的與法來寫 Shell Script , 而沒有
在 Shell Script 的第一列加上 #!/bin/sh , 那麼會發生什麼情形?
很簡單 , tcsh 會以為你寫的 Shell Script 是用 tcsh 的語法寫成
的 , 所以在解譯的過程中就會出現錯誤 . 但是若我們有加 #!/bin/sh
那麼 , tcsh 就會叫用 sh 來解譯你的 Shell Script , 自然就不會
有錯誤產生 .
我的建議是 : "無論如何 , 請明確的在 Shell Script 的第一列指出
這個 Shell Script 是用那一種 Shell 的語法寫成的 !"
(3.4) 變數
(3.4.1) 變數的指定
如同大部份的程式語言一樣 , Shell Programming Language 中也有
變數的存在 , 那我們要如何指定一個變數呢 ? 很簡單 , 如下:
  variable=value
舉幾個例子 :
  count=1
  X11_bin_path=/usr/X11/bin
但在指定變數時 , 有幾點要注意的 : 第一 , 等號的左右兩邊不要
有空格 , 譬如說 , 不可寫成 : color = blue , color= blue
color =blue , 一定得寫成 : color=blue . 第二 , 變數沒有所謂
的 "資料型態" . 當你指定變數時 , 變數只是被當成字元的組合來
看待 . 如上面的例子 : count=1 , count 放的是單純的 1 , 而不
像 C 語言中所謂 "整數型態的 1" 第三 , 變數指定的動作實際上
也可以在提示符號後直接作 . ( 如同 2.7 所提到的 )
(3.4.2) 變數的內容 : $variable
使用 echo $variable 可以顯示變數的內容 .
像剛剛的例子 , count=1 , 那麼 , 使用 echo $count 就會得到 1
使用 echo $X11_bin_path 就會得到 /usr/X11/bin
所以 , 我們可以了解到 : $variable 是變數的內容 !
我現在給一個問題 , 各位練習看看 : 把 var1 的內容設定成 10 ,
把 var2 的內容設定成 20 , 接著把 var2 的內容設定給 var1 .
  var1=10
  var2=20
  var1=$var2
  echo $var1
再實際練習一下 , 下面的四列與上面的四列有何不同 ?
  var1=10
  var2=20
  var1=var2
  echo $var1
(3.4.3) 變數與 Wildcard
你可以在提示符號下這樣試試 :
  list=*
  ls $list
上面的兩列 , 首先把 * 指定給 list , 所以 $list 就是 *
那 ls $list 就會變成 ls *
  five_char=?????
  ls $five_char
上面兩列會把長度為五個字元的檔案列出 .
(3.4.4) ${variable}
有人試了 (3.4.3) 中的例子之後就想到 , 我們可不可以這樣做 :
  file=mydoc
  cp $file $filenew                  
把 mydoc 檔案複製一份 , 並把新檔案的名稱後加上 new 變成
mydocnew . 假如你按照上面的方法來作 , 會有錯誤產生 .
這是因為 , $file 固然是 mydoc 沒錯 , 然而 $filenew 卻是
一個內容不知道是什麼的變數 . 所以我們應該這樣做 :
  file=mydoc
  cp ${file} ${file}new
其中 ${file} 會換成 mydoc , ${file}new 會換成 mydocnew  
(3.5) 引號
在 Shell Programming 中 , 很特殊的一點就是引號 , 引號有三種
分別為 ' ' 單引號  , " " 雙引號 , ` ` 反單引號 . 它們分別在
Shell Programming 中扮演不同但都相當重要的角色 . 我們分別敘
述如下 :
(3.5.1) 單引號
單引號最大的用處是 : 使得兩個單引號之間所夾的內容保持不變 .
比如說你想印出 : pig   cat      dog
那假如你這樣作 : echo pig   cat     dog
那麼結果仍是 pig cat dog
要改成 : echo 'pig   cat      dog'  才能達到目地
還有 , 在單引號中的特殊符號也會失去意義 :
  % echo '* is all'   
  * is all

  % color=blue
  % echo 'Is $color beautiful?'
  Is $color beautiful?
  % echo '> >  >  ?"
  There are 1 arguments
  argument1 is *  ?
  argument2 is
  argument3 is
  % argtest *     
  There are 64 arguments
  argument1 is a.c
  argument2 is c.c
  argument3 is e.c
  % argtest `cat friend_list`
  There are 3 arguments
  argument1 is veronica
  argument2 is jhhsu
  argument3 is ghguo
(3.7.3) $* 代表所有的參數
把剛剛的 Shell Script 再加以擴充如下 :
  #!/bin/sh
  echo "There are $# arguments"
  echo "argument1 is $1"
  echo "argument2 is $2"
  echo "argument3 is $3"
  echo "all arguments : $*"
拿這個 Shell Script 來試試看 :
  % argtest a b c
  There are 3 arguments
  argument1 is a
  argument2 is b
  argument3 is c
  all arguments : a b c
  % argtest 'a b c'
  There are 1 arguments
  argument1 is a b c
  argument2 is
  argument3 is
  all arguments : a b c
  % argtest "a b c"
  There are 1 arguments
  argument1 is a b c
  argument2 is
  argument3 is
  all arguments : a b c
  % argtest *
  There are 7 arguments
  argument1 is haha
  argument2 is haha1
  argument3 is haha2
  all arguments : haha haha1 haha2 memo1 memo13 memo2 memo23
(3.7.4) 參數的移動 shift
前面提過 , $n 是第 n 個參數的內容 , 但我們要特別注意的是 , n
必需小於 10 , 也就是說 , 不可能有 $10 這樣的東西 . 那假如我
們參數的個數很多 , 超過了 10 個 , 而又想取得超過 10 個後的參
數 , 要如何做 ? 答案就在 shift .
shift 是一個單純的命令 , 它把 $n 的內容放到 $n-1 中 . 如 $2
放到 $1 , $3 放到 $2 ...... 但 $1 並不會放到 $0 , 而是永遠的
消失 . 看下面的 Shell Script 及執行結果 :
  #!/bin/sh
  echo "There are $# arguments"
  echo "all argument : $*"  
  shift
  echo "There are $# arguments"
  echo "all argument : $*"  
  shift
  echo "There are $# arguments"
  echo "all argument : $*"
  shift
  echo "There are $# arguments"
  echo "all argument : $*"
  % argtest a b c d e f g h i j
  There are 10 arguments
  all argument : a b c d e f g h i j
  There are 9 arguments
  all argument : b c d e f g h i j
  There are 8 arguments
  all argument : c d e f g h i j
  There are 7 arguments
  all argument : d e f g h i j
所以從上面的例子得知 , 每 shift 一次 , 我們就可以在 $9 的地方
得到原來的第 10 個參數內容 . 如此一來 , 要取得第 10 個以後的參
數就一點也不困難了 . 還有 , $1 雖然會在 shift 後被丟掉 , 但假
如 $1 以後還要用到的話 , 我們仍然可以在 shift 前將它保留起來 :
    .
    .
    .
  temp=$1
  shift
    .
    .
    .                 
                 

Chap 4 -- Shell Programming 中的語法
在 Chap 3 學完之後 , 相信各位已經可以寫一些小的程式 . 但是總
好像少了些什麼 ? 不錯 , 這些可說是每種 Programming Language
中的靈魂 : 語法 ! 語法包含了條件邏輯的判斷 , 迴圈 , 函數呼叫
等 ......有了這些 , Shell Programming Language 才算完整 .
(4.1) if 結構
  if [ 測試條件 ]         
  then
     .
     .
  fi
  
我們可以很輕易的看到 , if 與 fi 之間就是典型的 if 結構
假如測試條件成立 , 就作 then 與 fi之間所夾的事情 .
還有其它許許多多的流程控制都要用到 "測試條件" , 在繼續
介紹這些流程控制之前 , 我們得先介紹測試條件 .
(4.2) 測試條件
測試條件大體上可分為 (I) 字串測試  (II) 整數測試
                     (III) 檔案測試  
(I) 字串測試 :
  string1 = string2       #  string1 等於 string2
  string1 != string2      #  string1 不等於 string2
  string                  #  string is not null
  -n string               #  string is not null
  -z string               #  string is null
   
  前面提過 , 變數設定可能像這種樣子 :  name=veronica
  那假如我們要把 name 與某一字串做相等比較 , 應該這樣做 :
    "$name" = veronica
   
  假如要作不相等比較 , 應該這樣做 :
    "$name" != veronica
  
  看下面的 Shell Script :
    #!/bin/sh
    name=veronica
    if [ "$name" = veronica ]
    then
      echo 'equal'
    fi
    if [ "$name" != veronica ]
    then
      echo 'not equal'
    fi

  執行結果 :
    % strcmp
    equal
  分析上面的 Shell Script , 首先 , 把 veronica 指定給 name
  此時 $name 就是 veronica . 執行到 if [ "$name" = veronica ]
  "$name" 就會換成 veronica , 所以變成 :
    if [ veronica = veronica ]     = int2   大於等於
  int1 -gt int2            #  int1 > int2    大於
  int1 -le int2            #  int1 &-
  then
    echo $?
  fi
  if w | grep "^veronica" > /dev/null
  then
    echo 'veronica is online'
  fi
  這個 Shell Script 值得好好研究 , 首先 , $? 代表上個命令的
  傳回值 . 假如在這個系統內 jhhsu 正在線上 , 而 veronica 並
  不在線上 . 那兩次 echo $? 的結果將會是 0 與 1 , 分別代表
  成功與不成功 . 還有 , 假如 if 所測試的是命令 , 那並不需要
  [  ] 號 . 前面為了方便 , 以及看起來有結構化 , 我把 if 的結構
  寫成 :
    if [ 測試條件 ]
    then
       .
       .
    fi
  
  但實際上 , 原來的結構應該是這種樣子 :
    if test_command
    then
       .
       .
    fi
  
  也就是說 , if 後接的必須是一個測試命令 , 而不是 [ 測試條件 ]
  所以 , 我們得用 test 這個命令來執行我們的測試條件 . 就像下面

    if test "$1" -gt 10
   
  但是 , test 測試條件 也可直接換成 [ 測試條件 ] , 所以我就寫
  成 if [ 測試條件 ]
  下面左右兩邊的 Shell Script 是完全相等的 :
                                  |
    #!/bin/sh                     |   #!/bin/sh
                                  |
    if [ "$#" -ne 2 ]             |   if test "$#" -ne 2
    then                          |   then
      echo 'needs two arguments'  |     echo 'needs two arguments'
      exit                        |     exit
    fi                            |   fi
                                  |              
    echo `expr $1 + $2`           |   echo `expr $1 + $2`
                                  |
  >&- 與 /dev/null 可參考 (2.5.2) , grep 可參考附錄二
(4.4) 組合測試條件的邏輯運算子
各位在看完 (4.3) 後 , 若你原本對管線 , 導向等等的課題有一定
的認識 , 那你已經可以寫出頗為有用的 Shell Script 了 .
在 (4.4) , 我們還要介紹三種邏輯運算子 not , and 與 or
(I) ! 代表反面的邏輯 : not
  也許你已經發現了 , 在 (4.2) 中 , 不管是字串的測試或者是整數
  的測試 , 都有反面的邏輯存在 :
    string1 = string2         # 字串1 與字串2 相等  
    string1 != string2        # 字串1 與字串2 不相等
    int1 -gt int2             # 整數1 大於 整數2
    int1 -le int2             # 整數1 小於等於 整數2
  但在檔案的測試方面 , 卻沒有反面的邏輯 . 此時 , 我們就要靠
  ! 來達成反面的測試 , 如 :
    if [ -f /etc/passwd ]     # /etc/passwd 是普通檔案嗎 ?
    if [ ! -f /etc/passwd ]   # /etc/passwd 不是普通檔案嗎 ?
  當然 , 在字串或整數比較方面 , 你也可以不用原來就已經有的運
  算子 , 而改用 ! 以代表反面的邏輯 . 請看下面的 Shell Script
    #!/bin/sh
    if [ ! "$1" -gt 10 ]      # 假如第一個參數的值不大於 10
    then
      echo 'argument is less than or equal to 10'
    fi

  執行結果 :
    % unary 3
    argument is less than or equal to 10
    % unary 11
   
(II) -a 代表邏輯上的 and
  講到目前為止 , 我們的比較都僅限於一個表示式 , 像某一變數的
  內容是否等於一個字串 , 或是某一變數的內容是否大於一個整數
  那假如我們想做如此的測試 : " 一個數字是否大於 10 而且小於
  100 , 也就是介於 10 到 100 之間 " 那我們就得用 -a 來達成 .
  請看下面的 Shell Script :
  
    #!/bin/sh
    if [ "$1" -gt 10 -a "$1" -lt 100 ]  # 10 '
    exit
  fi
  case "$1" in
  0) echo 'zero';;
  1) echo 'one';;
  2) echo 'two';;
  3) echo 'three';;
  4) echo 'four';;
  5) echo 'five';;
  6) echo 'six';;
  7) echo 'seven';;
  8) echo 'eight';;
  9) echo 'nine';;
  *) echo 'must a single digit';;
  esac   
執行結果 :
  % transnum 4
  four
  % transnum 18
  must a single digit
  % transnum veronica
  must a single digit
在上面的 Shell Script 中 , value 是第一個參數 , 而 pattern
的值分別為 0,1,2,3,4,5,6,7,8,9,* . 所以 , 當執行到 case 這
個結構時 , 第一個參數就與下面一個個 pattern 做比較 , 直到
遇見符合的 pattern , 就做 pattern 後面的事 . 比較特殊的是 ,
pattern 若是 * 號 , 則表示都不符合所要做的事 . 像上面 , 我
們只接受一位數字 , 假如你給與的是兩位數字以上 , 或是字串 ;
既然都不符合 0 到 9 的 pattern , 那就會顯示一段錯誤訊息 .
還有 , pattern 可以比較複雜 , 如下面的格式都是可以的 :
  [a-z]          /dev/null
  do
    sleep 60
  done
  echo "$1 has logged on"
上面是一個頗為有用的 Shell Script , 我相信各位可以看得懂前幾列
特別要解釋的是 until 的結構 . 在這個 Shell Script 中 , 我們
想做的事是 : 每 60 秒檢查一次 , 看看某 user 是不是在系統中 .
  until who | grep "^$1" > /dev/null
上面這列看起來很複雜 , 其實只要你熟悉命令及導向,管線 , 那實在
沒有什麼神秘的 . 我們知道 , until test_command 是只要
test_command 不成立 , 就一直做 do 與 done 之間的事 .
  who | grep "^$1" > /dev/null
把 who 的輸出交給 grep 去抓出看看有沒有第一個參數的人名 , 並
把應該正常輸出的訊息丟給 /dev/null 這個垃圾筒 .
假如沒有第一個參數的人名 , 就執行 sleep 60  ( sleep 60 是暫
停 60 秒的意思 ) , 假如有第一個參數的人名 , 就會脫離 until
結構 , 並且顯示那個人已經簽入系統 .
我們可以把這個 Shell Script 以背景執行 , 你會發覺它真的有用 .
(4.9) for 結構
  for var in word1 word2 ..... wordn
  do
       .
       .
  done
請各位注意 , Shell Programming 中的 for 與一般程式語言的 for
有很大的不同 . 我們能指定的包含 var , word1 , word2 ......
( word1 word2 .... wordn 為了方便 , 我把它稱為 word list )
以及 do 與 done 之間所做的事 . 我們還是先看一個例子 :
  #!/bin/sh
  for i in 1 2 peter bob
  do
    echo "$i"
  done
執行結果 :
  % fortest
  1
  2
  peter
  bob
我們所看到的是 , for 每執行一次 , 就把後面的資料指定給 var
像第一次執行時 , 1 被指定給 i , 第二次執行時 , 2 被指定給 i
第三次執行時 , peter 被指定給 i , 第四次執行時 , bob 被指定
給 i . 好了 , 現在我們把焦點放在 word1 word2 ..... wordn 上
它們是不是有什麼變化呢 ? 在這裡 , 我要提出幾種 :
(I) 使用命令製作出 word list
各位還記得 (3.5.3) 中 mail 給很多人的例子嗎 ?
  mail `cat member`  '
  read number1
  echo -n 'Input number2 --> '
  read number2
  echo `expr $number1 + $number2`

執行結果 :
  % plus3
  Input number1 --> 5
  Input number2 --> 10
  15
上面這個 Shell Script 可以由使用者任意輸入兩個變數 , read
分別把它們放到 number1 及 number2 , 最後再把 number1 及
number2 加起來 .
我再舉一個例子 , 你可以用 read 來製做出類似選單的東西 :
      
  #!/bin/sh
  cat  '
  read choice
  echo -n 'Input number1 -->'
  read number1
  echo -n 'Input number2 -->'
  read number2
  case $choice in
  1) echo `expr $number1 + $number2`;;
  2) echo `expr $number1 - $number2`;;
  3) echo `expr $number1 \* $number2`;;
  4) echo `expr $number1 / $number2`;;
  esac
  
上面是一個選單式的計算機 , 它可以選擇你要作的運算 , 並讀取
兩個數字來當運算元 . 這個 Shell Script 融合了我們在 (2.5.2)
中的 cat  '
    read choice
    echo -n 'Input number1 -->'
    read number1
    echo -n 'Input number2 -->'
    read number2
    case $choice in
    1)plus;;
    2)minus;;
    3)multi;;
    4)div;;
    *)echo 'Invalid choice'
      exit;;
    esac
  }
  main_select                       # 程式從這裡開始

使用這種函數寫作有幾點要注意 :
(I) 下面的情形 , 左邊可以正確的執行 , 右邊卻不行
    a_func()                |      a_func  
    {                       |
      b_func                |      a_func()
    }                       |      {
                            |        b_func
    b_func()                |      }
    {                       |
                            |      b_func()
    }                       |      {
                            |
    a_func                  |      }
(II) 命令列下參數的傳遞必須先把這些參數指定給另一個變數
     然後才能正確的工作 . 看下面的例子 :
     #!/bin/sh
     main()
    {
      echo $1                # error ?
      echo $2                # error ?
    }
    main
    上面的例子看起來沒錯 , 但實際上並不能 work , 你可能要
    改成下面的樣子 :
    #!/bin/sh
    main()
    {
      echo $number1
      echo $number2
    }
    number1=$1
    number2=$2
    main
   
(5.3) Shell Script 的偵錯
使用 sh -x shell_script_name  可以對一個 Shell Script 做
偵錯的動作 . 假如你的 Shell Script 不能正常的執行 , 或者是
執行的結果不符合你所預料的 . 出錯的地方可能為下列幾項之一 :
(I) 請檢查 Shell Script 是否有可讀及可執行權限 .
(II) 請注意 , Shell Programming Language 不是完全自由語法
     就像 if [ "$#" -ne 1 ]  就不能寫成 if ["$#" -ne 1]
     也不能寫成 if[ "$#" -ne 1 ] , 這些要特別注意 . 假如
     執行時有錯誤訊息 , 你要詳加檢查程式的語法結構 . 該
     空格的地方要有空格 .
(III) 要了解 , $variable 才是變數的內容 , 你很有可能把
      variable 當成變數的內容 .
(IV) 還有 , 你可能拼錯字了 , 這是很常見的 .
(V) 有些情況下 , 變數中的內容並不是你預期的 , 以致造成
    錯誤 . 此時你可以在程式中可疑的地方適時的加上一些
    偵錯點 , 把這些變數內容顯示出來看看 .
(V) 最後 , 邏輯上的錯誤是很難找到的 , 你可能也要注意 .
附錄一 : Shell Summary
-------------------------------------------------------------------
## 變數的指定 :
variable=value              
此時 , variable 是被當成字串看待 .
如 : NNTPSERVER=news.csie.nctu.edu.tw
     其中 , NNTPSERVER 是 variable ; news.csie.nctu.edu.tw 是 value
## 變數的內容 :
$variable
## 對 shell script 作偵錯執行的動作 :
sh -x [your_shell_script]
如 : sh -x search_string
     其中 , search_string 這個 shell script
     必需有 read 及 execute 的權限
## shell script 中的特殊符號 :
$# 參數個數
$n 第 n 個參數
$* 所有的參數
$@ 與 $* 一樣 , 除了每個參數都加上 "  "
$? 上一個命令傳回的值
$$ 目前此 shell 的 pid   // 當你在 script 中要 creat 一個檔 , 但不希望
                         // 這個檔案名是固定時 ( 尤其是有兩個人同時使用
                         // 這個 shell script ) 總不能 creat 的檔名取成
                         // 一樣 , 這時候 , $$ 就可派上用場
## 對整數作比較的運算子 :
int1 -eq int2            //    int1 = int2
int1 -ge int2            //    int1 >= int2
int1 -gt int2            //    int1 > int2
int1 -le int2            //    int1 '
  exit 1
fi
find . -inum "$1" -ok rm '{}' \;
---------------------- 字串比較的例子 -----------------------
#!/bin/sh
if [ "$#" -lt 1 ]
then
  echo 'again!'
  exit 1
fi
if [ "$1" = 'lala' ]
then
  echo 'lala is haha'
else
  echo 'unknown!!!'
fi
執行結果 :
%para3
again!
%para3 lala
lala is haha
%para3 dada
unknown!!!
--------------- 計算某一目錄底下有多少檔案或目錄 ------------
#!/bin/sh
if [ "$#" -ne 2 ]
then
  echo 'Usage : count_report  '
  exit
fi
case "$2" in
f)
  file=0
  for f in `find $1 -depth 1 -type f`
  do
    file=`expr ${file} + 1`
  done
  echo "Total files is ${file}"
  ;;
d)
  directory=0
  for f in `find $1 -depth 1 -type d`
  do
    directory=`expr ${directory} + 1`
  done
  directory=`expr ${directory} - 1`
  echo "Total directories is ${directory}"
  ;;
esac
---------- 一個比較長的例子 ( 可執行但功能並不完整 :p ) -----------
#!/bin/sh
COMPILER=cc
pause_until_enter()
{
  echo
  echo 'Press enter to continue ......'
  read enter_key
}
exit_shell_script()
{
  sleep 1
  clear
  exit
}
error_handle()
{
  if [ $? -ne 0 ]
  then
    echo "***** Some error occur , Please check ~/$$.err *****"
    echo -n "Display ~/$$.err now ? (y/n) --> "
    read error_select
    if [ $error_select = 'y' ]
    then
      cat ~/$$.err | more
      pause_until_enter
      case $main_select in
      1) modify_now;;
      esac
    fi
  fi
}  
modify_now()
{
  echo -n 'Modify your source code now ? (y/n) --> '
  read modify_now
  if [ $modify_now = 'y' ]
  then
    $EDITOR $source_code_name
  fi
}
read_source_code_name()
{
  echo -n 'Enter source codes name --> '
  read source_code_name
}
read_out_exe_file_name()
{
  echo -n 'Input executable filename , Press enter for a.out --> '
  read out_exe_file_name

  if [ "$out_exe_file_name" = ''  ]
  then
    echo 'Use default name : a.out'
  fi
}
read_library_name()
{
  echo -n 'Enter your library name --> '
  read library_name
}
read_obj_name()
{
  echo -n 'Enter your obj name --> '
  read obj_name
}
build_exe()
{
  read_source_code_name
  read_out_exe_file_name
  echo -n 'Link library? (y/n) --> '
  read link_lib_choice
  
  if [ "$link_lib_choice" = 'y' ]
  then
    echo -n 'Enter library name --> '
    read lib_name
  else
    lib_name=
  fi
  
  echo -n 'Creat symbol table for debugger? (y/n) --> '
  read debug_choice
  if [ "$debug_choice" = 'y' ]
  then
    $COMPILER -g -o ${outfile}.out $source_code_name $lib_name > ~/$$.err 2>&1
    error_handle
  else
    $COMPILER -o ${outfile}.out $source_code_name $lib_name > ~/$$.err 2>&1
    error_handle
  fi
}  
compile_only()
{
  read_source_code_name
  $COMPILER -c $source_code_name
}  
list_lib_content()
{
  read_library_name
  ar t $library_name | more
  pause_until_enter
}
add_obj_into_lib()
{
  read_library_name
  read_obj_name
  if [ -f $library_name ]
  then
    ar q $library_name $obj_name
  fi
}  
read_config()
{
  sed -n 1,1p comenv.conf
  pause_until_enter
}
main_menu()
{
  clear
  cat  '
  read main_select
  case $main_select in
  0);;
  1) compile_menu;;
  2) maintain_menu;;
  3) execute_menu;;
  4) config_menu;;
  5) exit_shell_script;;
  *) main_menu;;
  esac
}
compile_menu()
{
  clear
  cat  '
  read compile_select
  case $compile_select in
  1) build_exe;;
  2) compile_only;;
  3) main_menu;;
  4) exit_shell_script;;
  *) compile_menu;;
  esac
}
maintain_menu()
{
  clear
  cat  '
  read maintain_select
  case $maintain_select in
  1) list_lib_content;;
  2) add_obj_into_lib;;
  3) remove_obj_from_lib;;
  4) main_menu;;
  5) exit_shell_script;;
  esac
}
execute_menu()
{
  echo -n 'Enter execute file name --> '
  read execute_file_name
  $execute_file_name
  pause_until_enter
}
config_menu()
{
  clear
  cat  '
  read config_select
  case $config_select in
  1) read_config;;
  2);;
  esac
}
while test 0
do
  main_menu
done
附錄二 : grep 簡介
-------------------------------------------------------------------
grep -- 從檔案每一列中找出特定的樣板格式
grep [options] regular_expressions [files]
  options :
  -b  印出符合的那一列是在整個檔案中的第幾個 byte .

  -c  只印出共有多少列符合 regular_expressions .
  -h  當我們從許多檔案中找尋適合的條件時 , 假如找到了 , 就會印出是在
      那個檔案中找到的 . 然而 , 假如我們加了 -h 這個 option . 那麼 ,
      輸出的結果就不會告訴我們 , 是在那個檔案中找到符合的樣板 .
  -i  忽略 regular_expressions 中大小寫的差別 .              

  -l  只印出在那個檔案中有找到 , 而不印出檔案中符合的樣版 .
  -n  印出符合的樣板是在檔案中的第幾列 .
  -s  抑制錯誤訊息 , 像不存在的檔案啦 , 或者是不能讀的檔案 .
  -v  印出不符合 regular_expressions 的那些列 .
  example :
  從 test1 這個檔案中 , 找出含有 app 的列 :
                     
     % grep 'app' test1
  從 passwd 這個檔案中找出到底有多少人使用 tcsh :
     % grep -c '/bin/tcsh' /etc/passwd
  從目前的目錄下 , 找出任一列開頭具有 #include 的檔案 :
     % grep -l '^#include' *
  從 test2 這個檔案中 , 找出那些不含 class 的列 :
     % grep -v 'class' test2
  列出不含 pattern 的檔案 :
     % grep -c pattern files | grep :0 | cut -d":" -f1  
regular expressions  正規表示式
-------------------------------------------------------------
  
. 可符合任意一個字元 ( 包含空白字元 )
[] 可符合方括弧中列舉的字元
[^ regular_expressions] 不符合 regular_expression
* 與零個或更多個 * 前的字元吻合
.* 符合零或更多個字元
^regular_expressions  符合在每一列開頭的 regular_expressions
下面是 regular expressions 的一些例子
  regular expressions     match      
---------------------------------------------------------------
  ring                    ring , spring , ringing , stringing
                          ^^^^     ^^^^   ^^^^        ^^^^  
  
  .alk                    walk , talk ,  alk , skialk
                          ^^^^   ^^^^   ^^^^     ^^^^
[bB]ill                  bill , Bill , biller
                          ^^^^   ^^^^   ^^^^
number[5-9]              number7 , number90
                          ^^^^^^^   ^^^^^^^
[^ a-z]                  a21 , c1 , 123
                           ^^    ^   ^^^

ab*                      acc , ab , abb , abc
                          ^^^   ^^   ^^^   ^^^
ab.*                     ab , abb , abbc
                          ^^   ^^^   ^^^^
^T                       符合以 T 為開頭的每一列 ; This line
                                                    ^
參考資料 :
----------------------------------------------------------------
Shell :
1.  Title: Unix Shell Programming
    Authors: Stephen Kochan and Patrick Wood
    Publisher: Hayden
    Edition: 1990
    ISBN: 0-672-48448-X
2.  Title: The Unix C Shell Field Guide
    Authors: Gail Anderson and Paul Anderson
    Publisher: Prentice Hall
    Edition: 1986
    ISBN: 0-13-937468-X
General Unix Texts :
1.  Title: Unix Power Tools
    Authors: Jerry Peek, Tim O'Reilly and Mike Loukides (and other
    Publisher: O'Reilly / Bantam                       contributors)
    Edition: 1993
    ISBN: 0-553-35402-7
2.  Title: Unix in a Nutshell
    Authors: Daniel Gilly and O'Reilly staff
    Publisher: O'Reilly
    Edition: 2nd ed. 1992 (for System V and Solaris 2)
    ISBN: 1-56592-001-5
Programming :
1.  Title: Advanced Programming in The Unix Environment
    Author: Richard Stevens
    Publisher: Addison-Wesley
    Edition: 1992
    ISBN: 0-201-56317-7
Other :
1. UNIX FAQ
如何聯絡作者 :
  地址 : 交通大學十舍315R
  E-mail : jhhsu@csie.nctu.edu.tw
           u8217017@cc.nctu.edu.tw
假如您發現文章中有任何錯誤之處 , 請通知作者 .
還有 , 讀者的建議與批評也非常的歡迎 .                              


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/12857/showart_230865.html
  发表评论 查看评论(共有条评论) 我要提问
 
 


最新资讯更多>> 
· 德国公司推出Android平板电脑
· MySQL前任CEO就任Eucalyptus云..
· Google发布安全工具Skipfish
· 细数Linux平台上的“烂尾”非开..
· Novell拒绝私募基金20亿收购 造..
· 开源硬件能否像开源软件一样流行
· 甲骨文将关闭SUN著名开源项目O..
· 业界观察:收购之后MySQL前路多艰
· 负载均衡工具HAproxy 1.4.2发布
· Gnome Gmail: 将 Gmail 整合到..
论坛热点更多>> 
· ChinaUnix论坛Linux开发版块..
· 我的桌面怎么没有写入的权限..
· 谷歌关闭倒计时 中方警告其伙..
· ChinaUnix论坛Linux开发版块..
·
· sk_buff链表的问题
· 分区挂载挂不上,mount:you ..
· NF 结构LOCAL_OUT点 究镜 是..
· 求助!Netlink问题!
· 求教:关于 LVS 的粘滞问题!!!
文档更新更多>> 
· linux下u盘使用
· ubuntu dynamips 绑定网卡到虚拟机
· 虚拟机Debian中网卡无法启动
· 剖析SUSE Linux Tomcat自动启动
· Linux启动过程综述
· 分区大小调整完全手册
· 一步一学Linux与Windows 共享文件..
· 使用U盘安装Debian
· [Ubuntu-9.10] 修改 grub 以更改..
· cacti下利用thold插件来进行EMAI..
 
关于我们 | 联系方式 | 广告合作 | 诚聘英才 | 网站地图 | 友情链接 | 免费注册

Copyright © 2001-2009 ChinaUnix.net All Rights Reserved

感谢所有关心和支持过ChinaUnix的朋友们

京ICP证041476号