在前面的章節(jié)中,我們已經(jīng)裝備了一個命令行工具的武器庫。雖然這些工具能夠解決許多種計算問題, 但是我們?nèi)匀痪窒抻谠诿钚兄惺謩拥匾粋€一個使用它們。難道不是很棒,如果我們能夠讓 shell 來完成更多的工作? 我們可以的。通過把我們的工具一起放置到我們自己設計的程序中,然后 shell 就會自己來執(zhí)行這些復雜的任務序列。 通過編寫 shell 腳本,我們讓 shell 來做這些事情。
最簡單的解釋,一個 shell 腳本就是一個包含一系列命令的文件。shell 讀取這個文件,然后執(zhí)行 文件中的所有命令,就好像這些命令已經(jīng)直接被輸入到了命令行中一樣。
Shell 有些獨特,因為它不僅是一個功能強大的命令行接口,也是一個腳本語言解釋器。我們將會看到, 大多數(shù)能夠在命令行中完成的任務也能夠用腳本來實現(xiàn),同樣地,大多數(shù)能用腳本實現(xiàn)的操作也能夠 在命令行中完成。
雖然我們已經(jīng)介紹了許多 shell 功能,但只是集中于那些經(jīng)常直接在命令行中使用的功能。 Shell 也提供了一些通常(但不總是)在編寫程序時才使用的功能。
為了成功地創(chuàng)建和運行一個 shell 腳本,我們需要做三件事情:
編寫一個腳本。 Shell 腳本就是普通的文本文件。所以我們需要一個文本編輯器來書寫它們。最好的文本 編輯器都會支持語法高亮,這樣我們就能夠看到一個腳本關鍵字的彩色編碼視圖。語法高亮會幫助我們查看某種常見 錯誤。為了編寫腳本文件,vim,gedit,kate,和許多其它編輯器都是不錯的候選者。
使腳本文件可執(zhí)行。 系統(tǒng)會相當挑剔不允許任何舊的文本文件被看作是一個程序,并且有充分的理由! 所以我們需要設置腳本文件的權限來允許其可執(zhí)行。
為了保持編程傳統(tǒng),我們將創(chuàng)建一個 “hello world” 程序來說明一個極端簡單的腳本。所以讓我們啟動 我們的文本編輯器,然后輸入以下腳本:
#!/bin/bash
# This is our first script.
echo 'Hello World!'
對于腳本中的最后一行,我們應該是相當?shù)氖煜?,僅僅是一個帶有一個字符串參數(shù)的 echo 命令。 對于第二行也很熟悉。它看起來像一個注釋,我們已經(jīng)在許多我們檢查和編輯過的配置文件中 看到過。關于 shell 腳本中的注釋,它們也可以出現(xiàn)在文本行的末尾,像這樣:
echo 'Hello World!' # This is a comment too
文本行中,# 符號之后的所有字符都會被忽略。
類似于許多命令,這也在命令行中起作用:
[me@linuxbox ~]$ echo 'Hello World!' # This is a comment too
Hello World!
雖然很少在命令行中使用注釋,但它們也能起作用。
我們腳本中的第一行文本有點兒神秘。它看起來它應該是一條注釋,因為它起始于一個#符號,但是 它看起來太有意義,以至于不僅僅是注釋。事實上,這個#!字符序列是一種特殊的結(jié)構叫做 shebang。 這個 shebang 被用來告訴操作系統(tǒng)將執(zhí)行此腳本所用的解釋器的名字。每個 shell 腳本都應該把這一文本行 作為它的第一行。
讓我們把此腳本文件保存為 hello_world。
下一步我們要做的事情是讓我們的腳本可執(zhí)行。使用 chmod 命令,這很容易做到:
[me@linuxbox ~]$ ls -l hello_world
-rw-r--r-- 1 me me 63 2009-03-07 10:10 hello_world
[me@linuxbox ~]$ chmod 755 hello_world
[me@linuxbox ~]$ ls -l hello_world
-rwxr-xr-x 1 me me 63 2009-03-07 10:10 hello_world
對于腳本文件,有兩個常見的權限設置;權限為755的腳本,則每個人都能執(zhí)行,和權限為700的 腳本,只有文件所有者能夠執(zhí)行。注意為了能夠執(zhí)行腳本,腳本必須是可讀的。
當設置了腳本權限之后,我們就能執(zhí)行我們的腳本了:
[me@linuxbox ~]$ ./hello_world
Hello World!
為了能夠運行此腳本,我們必須指定腳本文件明確的路徑。如果我們沒有那樣做,我們會得到這樣的提示:
[me@linuxbox ~]$ hello_world
bash: hello_world: command not found
為什么會這樣呢?什么使我們的腳本不同于其它的程序?結(jié)果證明,什么也沒有。我們的 腳本沒有問題。是腳本存儲位置的問題。回到第12章,我們討論了 PATH 環(huán)境變量及其它在系統(tǒng) 查找可執(zhí)行程序方面的作用?;仡櫼幌?,如果沒有給出可執(zhí)行程序的明確路徑名,那么系統(tǒng)每次都會 搜索一系列的目錄,來查找此可執(zhí)行程序。這個/bin 目錄就是其中一個系統(tǒng)會自動搜索的目錄。 這個目錄列表被存儲在一個名為 PATH 的環(huán)境變量中。這個 PATH 變量包含一個由冒號分隔開的目錄列表。 我們可以查看 PATH 的內(nèi)容:
[me@linuxbox ~]$ echo $PATH
/home/me/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:
/bin:/usr/games
這里我們看到了我們的目錄列表。如果我們的腳本駐扎在此列表中任意目錄下,那么我們的問題將 會被解決。注意列表中的第一個目錄,/home/me/bin。大多數(shù)的 Linux 發(fā)行版會配置 PATH 變量,讓其包含 一個位于用戶家目錄下的 bin 目錄,從而允許用戶能夠執(zhí)行他們自己的程序。所以如果我們創(chuàng)建了 一個 bin 目錄,并把我們的腳本放在這個目錄下,那么這個腳本就應該像其它程序一樣開始工作了:
[me@linuxbox ~]$ mkdir bin
[me@linuxbox ~]$ mv hello_world bin
[me@linuxbox ~]$ hello_world
Hello World!
它的確工作了。
如果這個 PATH 變量不包含這個目錄,我們能夠輕松地添加它,通過在我們的.bashrc 文件中包含下面 這一行文本:
export PATH=~/bin:"$PATH"
當做了這個修改之后,它會在每個新的終端會話中生效。為了把這個修改應用到當前的終端會話中, 我們必須讓 shell 重新讀取這個 .bashrc 文件。這可以通過 “sourcing”.bashrc 文件來完成:
[me@linuxbox ~]$ . .bashrc
這個點(.)命令是 source 命令的同義詞,一個 shell 內(nèi)部命令,用來讀取一個指定的 shell 命令文件, 并把它看作是從鍵盤中輸入的一樣。
注意:在 Ubuntu 系統(tǒng)中,如果存在 ~/bin 目錄,當執(zhí)行用戶的 .bashrc 文件時, Ubuntu 會自動地添加這個 ~/bin 目錄到 PATH 變量中。所以在 Ubuntu 系統(tǒng)中,如果我們創(chuàng)建 了這個 ~/bin 目錄,隨后退出,然后再登錄,一切會正常運行。
這個 ~/bin 目錄是存放為個人所用腳本的好地方。如果我們編寫了一個腳本,系統(tǒng)中的每個用戶都可以使用它, 那么這個腳本的傳統(tǒng)位置是 /usr/local/bin。系統(tǒng)管理員使用的腳本經(jīng)常放到 /usr/local/sbin 目錄下。 大多數(shù)情況下,本地支持的軟件,不管是腳本還是編譯過的程序,都應該放到 /usr/local 目錄下, 而不是在 /bin 或 /usr/bin 目錄下。這些目錄都是由 Linux 文件系統(tǒng)層次結(jié)構標準指定,只包含由 Linux 發(fā)行商 所提供和維護的文件。
嚴肅認真的腳本書寫,一個關鍵目標是為了維護方便;也就是說,一個腳本可以輕松地被作者或其它 用戶修改,使它適應變化的需求。使腳本容易閱讀和理解是一種方便維護的方法。
我們學過的許多命令都以長短兩種選項名稱為特征。例如,這個 ls 命令有許多選項既可以用短形式也 可以用長形式來表示。例如:
[me@linuxbox ~]$ ls -ad
和:
[me@linuxbox ~]$ ls --all --directory
是等價的命令。為了減少輸入,當在命令行中輸入選項的時候,短選項更受歡迎,但是當書寫腳本的時候, 長選項能提供可讀性。
當雇傭長命令的時候,通過把命令在幾個文本行中展開,可以提高命令的可讀性。 在第十八章中,我們看到了一個特別長的 find 命令實例:
[me@linuxbox ~]$ find playground \( -type f -not -perm 0600 -exec
chmod 0600 ‘{}’ ‘;’ \) -or \( -type d -not -perm 0711 -exec chmod
0711 ‘{}’ ‘;’ \)
顯然,這個命令有點兒難理解,當?shù)谝谎劭吹剿臅r候。在腳本中,這個命令可能會比較容易 理解,如果這樣書寫它:
find playground \
\( \
-type f \
-not -perm 0600 \
-exec chmod 0600 ‘{}’ ‘;’ \
\) \
-or \
\( \
-type d \
-not -perm 0711 \
-exec chmod 0711 ‘{}’ ‘;’ \
\)
通過使用行繼續(xù)符(反斜杠-回車符序列)和縮進,這個復雜命令的邏輯性更清楚地描述給讀者。 這個技巧在命令行中同樣生效,雖然很少使用它,因為輸入和編輯這個命令非常麻煩。腳本和 命令行的一個區(qū)別是,腳本可能雇傭 tab 字符拉實現(xiàn)縮進,然而命令行卻不能,因為 tab 字符被用來 激活自動補全功能。
為書寫腳本配置 vim
這個 vim 文本編輯器有許多許多的配置設置。有幾個常見的選項能夠有助于腳本書寫:
:syntax on
打開語法高亮。通過這個設置,當查看腳本的時候,不同的 shell 語法元素會以不同的顏色 顯示。這對于識別某些編程錯誤很有幫助。并且它看起來也很酷。注意為了這個功能起作用,你 必須安裝了一個完整的 vim 版本,并且你編輯的文件必須有一個 shebang,來說明這個文件是 一個 shell 腳本。如果對于上面的命令,你遇到了困難,試試 :set syntax=sh。
:set hlsearch
打開這個選項是為了高亮查找結(jié)果。比如說我們查找單詞“echo”。通過設置這個選項,這個 單詞的每個實例會高亮顯示。
:set tabstop=4
設置一個 tab 字符所占據(jù)的列數(shù)。默認是8列。把這個值設置為4(一種常見做法), 從而讓長文本行更容易適應屏幕。
:set autoindent
打開 "auto indent" 功能。這導致 vim 能對新的文本行縮進與剛輸入的文本行相同的列數(shù)。 對于許多編程結(jié)構來說,這就加速了輸入。停止縮進,輸入 Ctrl-d。
通過把這些命令(沒有開頭的冒號字符)添加到你的 ~/.vimrc 文件中,這些改動會永久生效。
在這腳本編寫的第一章中,我們已經(jīng)看過怎樣編寫腳本,怎樣讓它們在我們的系統(tǒng)中輕松地執(zhí)行。 我們也知道了怎樣使用各種格式技巧來提高腳本的可讀性(可維護性)。在以后的各章中,輕松維護 會作為編寫好腳本的中心法則一次又一次地出現(xiàn)。
查看各種各樣編程語言的“Hello World”程序和實例:
這篇 Wikipedia 文章討論了更多關于 shebang 機制的內(nèi)容: