Sppence Murray 是 Linux 開發高手之一,同時長期以來他一直是 UNIX 的堅定支持者。本文介紹的是 Murray 和他在 Codemonks Consulting 的同事在日常的 Linux 開發以及應用服務工作中用到的基本技術: shell 腳本,相信 Linux 的開發人員都會受益於這項有用而且通用的技術。 Spence Murray 是 Codemonks Consulting 的創始人之一,自從 20 世紀 80 年代最早在 SunOS 上編寫代碼到現在,一直致力於 UNIX/Linux 的開發。從那時起,他曾在 IBM 公司的 AIX、SGI 公司的 Irix 工作,長時間地編寫跨平台的 UNIX 代碼,包括 HP/UX,Irix,Solaris/SunOS,SCO UNIX,各種 BSD,MacOS X,當然,還有 Linux。從圖形/視頻設備驅動程序到 UI 代碼,他什麼工作都做過。Murray 編寫的跨平台代碼包括 X Window System Xserver 代碼,以及作為 Netscape Navigator 一部分的核心浏覽器代碼。 Murray 最經常使用的 Linux 工具是 vi、bash 和 Emacs。“不論我是在寫 C、C++、Java、shell 腳本,還是 Html,大部分的時間我都在這些工具中來回切換”,他說。
Linux 秘密武器 Murray 認為,對一個 Linux 開發人員來說,shell 是一個強大的軟件開發工具,無論怎麼評價都不過分。“在我做的每一項工作中都要用到 shell 腳本,不論是快速地閱讀和修改普通文本還是編寫代碼”,他說。“它輕便而快捷,它短小的命令使得來回移動代碼稱為一個迅速而沒有痛苦的過程。作為一名編輯,它很快就會成為第二本能”。 對 Murray 來說,Emacs 作為一個開發工具出現的晚了一些。“在 90 年代早期,我嘗試使用 Emacs 作為一個 IDE,並很快就轉換門廳。Emacs 非常強大,在那些日子裡,我會一直開著一個 Emacs 窗口,經常打開幾十個源文件,每個都有我編輯的上下文、使用 gdb 的調試會話以及在不同的源目錄下運行的 bash 腳本。有很多關於 Emacs 的資料,可以說,這是個可怕的工具...再者,您可以在任何您想要花時間去做開發的系統上運行 Emacs。 自從 20 世紀 80 年代中期第一次使用 SunOS支持的 vi 這個簡潔的環境以來,Emacs 編輯器已經成為了 Murray 的標准工具。"它在各種流派的 UNIX 上都可以使用,這是我在致力於跨平台的開發工作時選擇它的主要原因之一”,他說。
Linux 開發人員:了解您的 shell Murray 要求您要了解您的 shell。“Bash、tcsh、csh――shell 是您最基本的軟件開發工具”,他強調說。“它可以做許多了不起的事情。所有的工作都要依賴於它……和它的強大功能”。作為說明通用的 shell 腳本功能強大的例子,在參考資料部分中有一個可以下載的文件,其中有一組腳本,用於獲得 Red Hat 發行的更新 RPM 軟件包並將它們合並到原來的軟件包和定制的軟件包。下載文件並解壓縮後,您可以在 /developerworks/rpm_update_scripts 目錄下找到腳本。最終結果是一個包括所有軟件包最新版本的目錄和一個用於網絡安裝的升級的 hdlist 文件。 下面的代碼片段實現的是對 Red Hat RPM 軟件包的自動更新,以創建一個使用最新的 RPM 的可以安裝的版本。這對任何一個維護公共 Linux 服務器的人來說是一個基本的步驟。就我們而言,我們通常是維護許多公共 Linux 服務器上的大量網絡服務。下面是可以自動完成更新最新的安全和功能的過程的部分腳本。 下面的腳本樣例證明了普通的 shell 編程技術可以廣泛應用於各種系統配置和程序設計應用。腳本使用的是 bourne shell,它是在不同的 UNIX 系統中最為常見的 shell。這樣就可以保證這些非常輕便的代碼可以稍加修改或者不加修改地在不同的 UNIX 系統上使用。修改 Red Hat 軟件包的規范以應用於其它 Linux 發行版本是很容易的。 freshen.sh 使用指定的 RPM FTP 更新站點上的 RPM 軟件包來更新原有的 RPM 列表。執行過濾器來替換更新 RPM 軟件包。最後,長長的發行列表根據從更新鏡像站點上得到的新 RPM 軟件包完成更新。 清單 1. fresh.sh #!/bin/sh rh_ver=$1 rh_path=$2 update_dir=${rh_path}/RH${rh_ver}-updates custom_dir=${rh_path}/RH${rh_ver}-custom install_dir=${rh_path}/RH${rh_ver}-install # Sanity check for the original Directory. # Create update and install directories if they don't exist [ -d ${update_dir} ] mkdir ${update_dir} [ -d ${install_dir}/RedHat/RPMS ] mkdir -p ${install_dir}/RedHat/RPMS # Get latest updates from fresh rpms FTP site ./get_update.sh ${rh_ver} ${update_dir} # Create/update hardlinks from update, and custom directories # to the install directory. We assume that original RPMS are already # hardlinked to the install directory, so all we need to do is filter # out any replaced by updated packages. ./do-links.sh ${update_dir} ${install_dir}/RedHat/RPMS [ -d ${custom_dir} ] && ./do-links.sh ${custom_dir} ${install_dir}/RedHat/RPMS # Filter out all but the latest version of everything. ./filter-rpms.pl $install_dir/RedHat/RPMS # Rebuild the hard disk lists /usr/lib/anaconda-runtime/genhdlist ${install_dir} freshen.sh 調用 do-links.sh 和 get_update.sh ,分別去設置 RPM 發行版本的源、宿(省略了源 RPM 軟件包;硬鏈接用來設置目的 RPM)和檢索更新。 清單 2. do-links.sh #!/bin/sh src=$1 dest=$2 #for file in $src/*; do for file in `find $src -name *.rpm -a ! -name *.src.rpm -print`; do base=`basename $file;` if test ! -f $dest/$base; then echo "Linking $file"; ln $file $dest else echo "EXISTS: $file"; fi done 清單 3. get_update.sh #!/bin/sh rh_ver=$1 dest=$2 echo "Retrieving updates for version ${rh_ver} to $dest" lftp 2 (__GNUC__ == 2 && __GNUC_MINOR__ > 4) #define PRINTF(f, a) __attribute__((format (printf, f, a))) #else #define PRINTF(f,a) #endif extern hoed_buf_t *hoed_buf_alloc(int init_size, int max_size); extern void hoed_buf_free(hoed_buf_t *); extern void hoed_buf_reset(hoed_buf_t *); extern void hoed_buf_new_string(hoed_buf_t