認識 BASH Shell
本文已不再維護,新文章請參考 這裡
最近更新日期:2003/02/10
什麼是 Shell
BASH Shell
變數與變數的設定echo, env, set, 變數設定規則, export, unset, 變數的有效範圍, read, array, $RANDOM, eval, 重要的環境變數(?, HOME, SHELL, MAIL, HISTSIZE...)
命令別名與歷史命令alias(設定命令別名), history, !command,
bash shell 的設定檔案source,
萬用字元與特殊符號
絕對路徑與相對路徑
命令重導向 ( redirect )
管線命令 ( pipe )cut, sort, wc, uniq, tee, tr, split,
本章習題練習

什麼是 Shell
這應該是個蠻有趣的話題:『什麼是 Shell ?』相信只要摸過電腦,對於作業系統(不論是 Linux 、 Unix 或者是 Windows )有點概念的朋友們大多聽過這個名詞,因為只要有『作業系統』那麼就離不開 Shell 這個東西。不過,在討論 Shell 之前,我們先來瞭解一下電腦的運作狀況吧!舉個例子來說:當你要電腦傳輸出來『音樂』的時候,你的電腦需要什麼東西呢
  1. 當然就是需要你的硬體有『音效卡晶片』這個硬體配備,否則怎麼會有聲音;
  2. 作業系統的核心可以支援這個晶片組,當然還需要提供晶片的驅動程式囉;
  3. 需要使用者(就是你)輸入發生聲音的指令囉!
這就是基本的一個輸出聲音的需要的步驟!那麼也就是說,你必須要『輸入』一個指令之後,『硬體』才會透過你下達的指令來工作!嘿嘿!那麼硬體如何知道你下達的指令呢?那就是 kernel (核心)的控制工作了!瞭解了嗎?沒錯!也就是說,我們必須要透過『 Shell 』將我們輸入的指令與 Kernel 溝通,好讓 Kernel 可以控制硬體來正確無誤的工作!基本上,我們可以透過底下這兩張圖來說明一下:

基本上,替我們工作的是『硬體』,而控制硬體的是『核心』,再來,我們使用者乃是利用『Shell』控制一些 kernel 提供的 『工具 Utility』來操控硬體替我們正確的工作。再進一步來說,由於 kernel 聽不懂人類的語言,而人類也沒有辦法直接記得 kernel 的語言,所以兩者的溝通就得藉由 shell 來支援了!(其實早期的 DOS 的文字介面也是使用 shell 來溝通呀!那個 shell 的名稱就叫做 command.com ,還記得嗎? ^_^)
 
以字面上的意思來說, kernel 是『核心』的意思,而 Shell 是『殼』的意思,呵呵!也就是說, shell 是最外頭的咚咚!而 kernel 乃是最內層的的咚咚啦!核心是作業系統的最底層的東西!這個核心裡頭包括了各種的支援硬體的工具!當然囉,如果你的硬體太新,而你的 kernel 並沒有支援的話,那麼很抱歉,你的 Shell 能力再怎麼強,也沒有辦法使硬體工作的!這樣可以瞭解了嗎?呵呵!沒錯!使電腦主機工作的正是核心的任務,但是操作核心來替使用者工作的,卻是 shell 喔!因此,有時候你的 shell 搞了老半天,硬體卻不能工作的時候,請注意,您的『核心』是否正確呢?阿!扯遠了!這是 kernel 章節才要說的東西??

BASH Shell
知道什麼是 Shell 之後,那麼我們來瞭解一下 Linux 使用的是哪一個 shell 呢?什麼!哪一個?難道說 shell 不就是『一個 shell 嗎?』哈哈!那可不!由於早年的 Unix 年代,發展者眾,所以由於 shell 依據發展者的不同就有許多的版本,例如常聽到的 Bourne SHell (sh) 、在 Sun 裡頭預設的 C SHell、 商業上常用的 K SHell、, 還有 TCSH 等等,每一種 Shell 都各有其特點。至於 Linux 使用的這一種版本就稱為『 Bourne Again SHell (簡稱 bash ) 』,這個 Shell 是 Bourne Shell 的增強版本,也是基準於 GNU 的架構下發展出來的呦!

在介紹 shell 的優點之前,先來說一說 shell 的簡單歷史吧:第一個流行的 shell 是由 Steven Bourne 發展出來的,為了紀念他所以就稱為 Bourne shell ,或直接簡稱為 sh !而後來另一個廣為流傳的 shell 是由柏克萊大學的 Bill Joy 設計依附於 BSD 版的 Unix 系統中的 shell ,這個 shell 的語法有點類似 C 語言,所以才得名為 C shell ,簡稱為 csh !由於在學術界 Sun 主機勢力相當的龐大,而 Sun 主要是 BSD 的分支之一,所以 C shell 也是另一個很重要而且流傳很廣的 shell 之一(因為太多的程式設計師使用的就是 C 語言啦!)!

好了,那麼 BASH 是怎麼一回事呢?這個 shell 是 GNU 計畫中重要的工具軟體之一,目前也是 GNU 作業系統中標準的 shell ,他主要相容於 sh ,並且依據一些使用者需求,而加強的 shell 版本,可以說目前幾乎所有的 Linux distribution 都是使用 bash 作為管理核心的主要 shell !因此,不論您使用的是那個 distribution ,你都難逃需要學習 bash 的宿命啦!那麼這個 shell 有什麼好處,幹嘛 Linux 要使用他作為預設的 shell 呢? BASH 主要的優點有底下幾個:

在瞭解了 BASH 的優點之後,再來我們要來討論的是:那如何在 Shell 提供的環境中下達指令呢?其實很簡單的,下達指令的方式為:
[root@test /root]# command [-options] parameter1 parameter2 ...
                     指令     選項      參數(1)    參數(2)

1. command 為指令的名稱,例如變換路徑的指令為 cd 等等;
2. 中刮號[]並不存在於實際的指令中,而加入參數設定時,通常為 - 號,有時候完整名稱會輸入 -- 符號;
3. parameter1 parameter2.. 為依附在 option 後面的參數,或者是 command 的參數;
4. command, -options, parameter1.. 這幾個咚咚中間以空格來區分,不論空幾格 shell 都視為一格;
5. 指令太長的時候,可以使用 \ 符號來跳脫 [Enter] 符號,使指令連續到下一行。

實例:
[root@test /root]# ls -al /root   <==以 ls 列出 /root 這個目錄中的隱藏檔與相關的屬性參數;
[root@test /root]# ./configure --prefix=/usr/local --with-tcp_wrappers \
> --with-pam     <==這兩行實際上是同一行的指令,但是加上 \ 跳脫符號後,指令可以連續到下一行!
[root@test /root]# ls           -al   /root  <==這個指令與第一個相同,空白字元不論幾個,僅視為一個來處理。

很簡單吧!OK!那麼再來一個問題:『Shell 是什麼時候開始接管 Linux 主機的!?』我們在後面會再提到『開機流程』的介紹,這裡先跳過去,假設你的機器已經開機成功了,那麼主機便進入等待使用者 login 的狀態。當使用者輸入了帳號與密碼,並且順利的 pass 之後,經過了 shell 的環境變數檔案讀取功能,最後,使用者進入自己的『家目錄』之後,例如 root 的家目錄在 /root 底下,一般使用者的家目錄則在 /etc/passwd 這個檔案裡面規定,那麼主機就已經丟了一個程序稱為 bash 的給你操作囉!

變數與變數的設定:echo, env, set, 變數設定規則, export, unset,
再繼續研究 BASH 之前,我們要就變數這個東西來討論一番,因為在主機裡面有太多的資料需要進行存取了,而這些資料都是一些服務所必須的,例如 mail 的存取路徑在 /var/spool/mail 、家目錄預設在 /home/useraccount 等等,當然我們可以改變這些個變數,但是如果該變數是直接深植於套件當中,那麼當你修改了某些參數之後,嘿嘿!你的套件就必須要『由原始碼直接更新再編譯』才行!這樣似乎很麻煩,所以囉,就會有變數這個好東西出來了!
 
舉個簡單的例子來說, sendmail 的 smtp 存放 mail 路徑是經由 /etc/profile 裡頭的
MAIL="/var/spool/mail/$USER"
來設定的,而當我修改了上面這一個咚咚,然後重新開機之後,嘿嘿嘿嘿!我的郵件就可以存放到不同的路徑去了!而且不會有問題!可以順利的『在 Linux 主機上面』收發。然而問題發生在 pop3 這個服務上面,由於 pop3 的預設路徑是在 source code 裡頭,而且就正是 /var/spool/mail 這個路徑,也就是說,不論我怎麼修正我的『變數』, pop3 都不為所動!唉∼真慘,所以就無法直接以 pop3 來收信了(例如 OutLook 就不能工作了)!會發生密碼不接受的問題呢!
此外,例如我們在執行程式的時候,系統怎麼知道你的 ls 這個指令放在哪裡?原來是有 PATH 這個變數,系統會透過這個變數裡面所設定的路徑去依序尋找該指令系統,如果找不到的話,那麼才在螢幕上顯示『 command not found 』字樣!這些還都只是系統預設的變數的目的,如果是個人的設定方面:例如你要寫一個大型的 script (批次檔)時,有些資料因為可能由於使用者習慣的不同而有差異,比如說路徑好了,由於該路徑在 script 被使用在相當多的地方,如果下次換了一部主機,都要修改 script 裡面的所有路徑,那麼我一定會瘋掉!這個時候如果使用變數,而將該變數的定義寫在最前面,後面相關的路徑名稱都以變數來取代,嘿嘿!那麼你只要修改一行就等於修改整篇 script 了!方便的很!所以,良好的程式設計師都會善用變數的定義!(這個部分我們在底下還會再提到!)
 
如果說的學理一點,那麼由於在 Linux System 下面,所有的執行續都是需要一個執行碼,而就如同上面提到的,你『真正以 shell 來跟 Linux 溝通,是在正確的登入 Linux 之後!』這個時候你就有一個 bash 的執行程序,也才可以真正的經由 bash 來跟系統溝通囉!而在進入 shell 之前,也正如同上面提到的,由於系統需要一些變數來提供他資料的存取(或者是一些環境的設定參數值,例如是否要顯示彩色等等的),所以就有一些所謂的『環境變數』需要來讀入系統中了!這些環境變數例如 PATH、HOME、MAIL、SHELL等等,都是很重要的,為了區別與自訂變數的不同,環境變數通常以大寫字元來表示呢!
 
說了那麼久,那麼到底『什麼是變數』呢?簡單的說,『變數就是以一組文字或符號等,來取代一些設定或者是一串保留的資料!』,例如:『VBird』就是『鳥哥』,所以當你讀取 VBird 的時候,系統自然就會知道!哈!那就是鳥哥!最簡單的例子可以取 PATH 來說明!如果你對於『相對路徑與絕對路徑』還有點印象的話,那麼應該曉得『要下達正確的指令,應該需要指定路徑與檔名』才行!例如你的 ls 指令應該需要以『/bin/ls』來下達指令才對,那麼為何你在任意的路徑下都可以執行 ls 呢?而不需要指定路徑呢?這是因為系統已經預設了一些『搜尋路徑(PATH)』了,所以當你需要執行一些指令的時候,系統就會依照該 PATH 的設定來進行指令的搜尋!而這個 PATH 就是所謂的變數了!那麼如何『顯示變數』呢?這就需要使用到 echo 這個指令啦!

命令別名與歷史命令
bash shell 的設定檔案:
萬用字元與特殊符號

絕對路徑與相對路徑
命令重導向
好了,對於『 > , >> 』這兩個東西有一定的概念之後,我們來深入的談一談『命令輸出重導向』的觀念吧!如前所述,基本上, Linux 執行的結果中,可以約略的分成『正確輸出』與『錯誤輸出』兩種方式。例如,當你以一般身份執行 find 這個指令時,例如執行『 find / -name testing 』時,由於你是一般身份,又有些資料夾是不允許一般身份者進入的,所以囉,當你使用 find 時,就會有錯誤訊息發生了!但同時如果有 testing 這個檔案在你可以進入的資料夾當中,那麼螢幕也會輸出到給你看!因此,就具有正確的與錯誤的輸出兩種囉!(分別稱為 Stdout 與 Stderror)例如下面為執行結果:裡面的『 find: /home/root: Permission denied 』就告訴你該資料夾你沒有權限進入,這就是錯誤的輸出了,那麼『 /home/test/tseting 』就是正確的輸出了!
 
[test @test test]# find / -name testing
find: /home/test1: Permission denied    <==這是錯誤的輸出
find: /home/root: Permission denied       <==這是錯誤的輸出
find: /home/masda: Permission denied      <==這是錯誤的輸出
/home/test/testing                        <==這是『正確』的輸出
[test @test test]# 

好了,那麼假如我們想要將資料輸出到 list 這個檔案中呢?執行『 find / -name testing > list 』會有什麼結果?呵呵,你會發現 list 裡面存了剛剛那個『正確』的輸出資料,至於螢幕上還是會有錯誤的訊息出現呢!傷腦筋!如果想要將正確的與錯誤的資料分別存入不同的檔案中需要怎麼做?!呵呵!其實在資料的重導向方面,正確的寫法應該是『 1> 』與『 2> 』才對!但是如果只有 > 則預設是以 1> 來進行資料的!那個 1> 是輸出正確資料, 2> 則是錯誤資料輸出項目。也就是說:

好了,那麼上面的例子中,我們如何將資料輸出到不同的地方去呢?可以這麼寫:
 
[test @test test]# find / -name testing 1> list_right 2> list_error

這樣一來,剛剛執行的結果中,有 Permission 的那幾行錯誤資訊都會跑到 list_error 這個檔案中,至於正確的輸出資料則會存到 list_right 這個檔案中囉!這樣可以瞭解了嗎?如果有點混亂的話,去休息一下再來看看吧!!


管線命令 ( pipe )
本章習題練習 ( 要看答案請將滑鼠移動到『答:』底下的空白處,按下左鍵圈選空白處即可察看 )
參考文章:
2002/06/27:第一次完成
2003/02/10:重新編排與加入 FAQ