shell歷史
Shell的作用是解釋執行用戶的命令,用戶輸入一條命令,Shell就解釋執行一條,這種方式稱爲交互式(Interactive),Shell還有一種執行命令的方式稱爲批處理(Batch),用戶事先寫一個Shell腳本(Script),其中有很多條命令,讓Shell一次把這些命令執行完,而不必一條一條地敲命令。Shell腳本和編程語言很相似,也有變量和流程控制語句,但Shell腳本是解釋執行的,不需要編譯,Shell程序從腳本中一行一行讀取並執行這些命令,相當於一個用戶把腳本中的命令一行一行敲到Shell提示符下執行。
由於歷史原因,UNIX系統上有很多種Shell:
1. sh(Bourne Shell):由SteveBourne開發,各種UNIX系統都配有sh。
2. csh(C Shell):由Bill Joy開發,隨BSD UNIX發佈,它的流程控制語句很像C語言,支持很多BourneShell所不支持的功能:作業控制,命令歷史,命令行編輯。
3. ksh(Korn Shell):由David Korn開發,向後兼容sh的功能,並且添加了csh引入的新功能,是目前很多UNIX系統標準配置的Shell,在這些系統上/bin/sh往往是指向/bin/ksh的符號鏈接。
4. tcsh(TENEX CShell):是csh的增強版本,引入了命令補全等功能,在FreeBSD、MacOS X等系統上替代了csh。
5. bash(Bourne AgainShell):由GNU開發的Shell,主要目標是與POSIX標準保持一致,同時兼顧對sh的兼容,bash從csh和ksh借鑑了很多功能,是各種Linux發行版標準配置的Shell,在Linux系統上/bin/sh往往是指向/bin/bash的符號鏈接。雖然如此,bash和sh還是有很多不同的,一方面,bash擴展了一些命令和參數,另一方面,bash並不完全和sh兼容,有些行爲並不一致,所以bash需要模擬sh的行爲:當我們通過sh這個程序名啓動bash時,bash可以假裝自己是sh,不認擴展的命令,並且行爲與sh保持一致。
itcast$ vim /etc/passwd
其中最後一列顯示了用戶對應的shell類型
root:x:0:0:root:/root:/bin/bash
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
itcast:x:1000:1000:itcast,,,:/home/itcast:/bin/bash
ftp:x:115:125:ftp daemon,,,:/srv/ftp:/bin/false
用戶在命令行輸入命令後,一般情況下Shell會fork並exec該命令,但是Shell的內建命令例外,執行內建命令相當於調用Shell進程中的一個函數,並不創建新的進程。以前學過的cd、alias、umask、exit等命令即是內建命令,凡是用which命令查不到程序文件所在位置的命令都是內建命令,內建命令沒有單獨的man手冊,要在man手冊中查看內建命令,應該執行
itcast$ manbash-builtins
如export、shift、if、eval、[、for、while等等。內建命令雖然不創建新的進程,但也會有Exit Status,通常也用0表示成功非零表示失敗,雖然內建命令不創建新的進程,但執行結束後也會有一個狀態碼,也可以用特殊變量$?讀出。
執行腳本
編寫一個簡單的腳本test.sh:
#! /bin/sh
cd ..
ls
Shell腳本中用#表示註釋,相當於C語言的//註釋。但如果#位於第一行開頭,並且是#!(稱爲Shebang)則例外,它表示該腳本使用後面指定的解釋器/bin/sh解釋執行。如果把這個腳本文件加上可執行權限然後執行:
itcast$ chmod a+xtest.sh
itcast$ ./test.sh
Shell會fork一個子進程並調用exec執行./test.sh這個程序,exec系統調用應該把子進程的代碼段替換成./test.sh程序的代碼段,並從它的_start開始執行。然而test.sh是個文本文件,根本沒有代碼段和_start函數,怎麼辦呢?其實exec還有另外一種機制,如果要執行的是一個文本文件,並且第一行用Shebang指定了解釋器,則用解釋器程序的代碼段替換當前進程,並且從解釋器的_start開始執行,而這個文本文件被當作命令行參數傳給解釋器。因此,執行上述腳本相當於執行程序
itcast$ /bin/sh./test.sh
以這種方式執行不需要test.sh文件具有可執行權限。
如果將命令行下輸入的命令用()括號括起來,那麼也會fork出一個子Shell執行小括號中的命令,一行中可以輸入由分號;隔開的多個命令,比如:
itcast$ (cd ..;ls -l)
和上面兩種方法執行Shell腳本的效果是相同的,cd..命令改變的是子Shell的PWD,而不會影響到交互式Shell。然而命令
itcast$ cd ..;ls -l
則有不同的效果,cd ..命令是直接在交互式Shell下執行的,改變交互式Shell的PWD,然而這種方式相當於這樣執行Shell腳本:
itcast$ source ./test.sh
或者
itcast$ . ./test.sh
source或者.命令是Shell的內建命令,這種方式也不會創建子Shell,而是直接在交互式Shell下逐行執行腳本中的命令。