作者: Sppence Murray 出處: IBM
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 << EOF;
open ftp.freshrpms.net
mirror -n pub/redhat/linux/updates/${rh_ver}/en/os/i386 $dest/i386;
mirror -n pub/redhat/linux/updates/${rh_ver}/en/os/i486 $dest/i486;
mirror -n pub/redhat/linux/updates/${rh_ver}/en/os/i586 $dest/i568;
mirror -n pub/redhat/linux/updates/${rh_ver}/en/os/i686 $dest/i686;
mirror -n pub/redhat/linux/updates/${rh_ver}/en/os/SRPMS $dest/SRPMS;
mirror -n pub/redhat/linux/updates/${rh_ver}/en/os/noarch $dest/noarch;
Java 和 Linux
在Codemonks,相當多的開發工作是在 Linux 上用 Java 完成。這兩個工具的組合為創建商業級質量的 Web 應用提供了一個平台,Murray 說。“在做這些項目的過程中,我們發現我們要總體上了解客戶已有的應用代碼”,他回憶說。locks.c (在下載得到的壓縮文件中的 /developerworks/locks 目錄下) 是一個代碼片段,實現的是用於 Java Virtual Machine Profiler Interface (JVMPI) 的讀/寫鎖以及大量的調試代碼。
Linux 開發人員的代表
“在情況允許的時候,不要寫特定於系統的代碼”,Murray 說,而是克服困難去“寫好的跨平台的代碼”。受雇的 Murray 堅持認為他最大的資本永遠是“寫具有商業品質的代碼,構建和提供網絡服務,定制 OS 或內核,而且完全基於可靠的開放源代碼的平台”。
下面是一個代碼片段,來自於一個跨平台的定制的 IMAP 服務器,這個服務器由 Linux 和 MacOS X 的開發人員共同開發。代碼實現的是一個用來處理字符串的簡單的增長緩存。這樣避免了緩存溢出的問題(不要忘記那些安全漏洞),而不必要您每次做某些事情的時候重新分配空間。它是通過維護一個簡單的可變長的緩存來實現的,這個緩存可以寫滿和清空。這個緩存已經被用於一個實驗用的 IMAP 服務器,這個服務器是由一個團隊緊張工作了一周完成的。
除了一個簡單的字符串緩沖區的實現之外,這段代碼還實現了一個可變大小的字符串數組。它完成的是一個簡單的接口,當您寫完一個字符串以後,您可以標記它然後繼續寫下一個。此外,這樣會節約空間分配,並且將比較亂的代碼組織到一起。
完整的 IMAP 服務器的代碼將在今年某個時間發布。
清單 4. 定制的 IMAP 服務器一部分
#ifndef HOED_BUF_H;
#define HOED_BUF_H;
typedef struct {;
char *str
int size
int length
int str_start
int max_size
int n_strings
int size_strings
int *str_posn
char **str_set
} hoed_buf_t
#if;;__GNUC__ > 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 *)
extern char **hoed_buf_get_set(hoed_buf_t *, int *n_string)
extern char *hoed_buf_put_char(hoed_buf_t *, char toadd)
extern char *hoed_buf_sprintf(hoed_buf_t *, const char *format,...);