Linux中大量使用指令碼語言,而不是C語言!

2020-07-16 10:05:09
說到在 Linux 下的程式設計,很多人會想到用C語言,Linux 的核心、shell、基礎命令程式,也的確是用C語言編寫的,這首先證明了一點,C語言很強很通用。

到目前為止,C語言依然壟斷著計算機工業中幾乎所有的系統程式設計,而且也正因為是C語言,才使得 Unix 以及後來的 Linux 能夠這麼廣泛地被人們去研究、去改進、去製作自己的分支,以至於我們能在各種硬體平台上使用它們。

但是細心的人會發現,Linux 啟動過程中所涉及的各種程式,很少有C語言的痕跡。它們大多是指令碼程式。不單單在啟動過程中是這樣,那些用於安裝軟體的工具 yum、apt-get,甚至是 configure 和 Makefile 也都是指令碼程式。而且你可能還沒注意到,那些用於系統管理的工具,如設定 ADSL 撥號上網的工具、設定守護行程的工具等,很多也都是指令碼程式。

大量使用指令碼程式,是所有類 Unix 系統不同於其他系統的一個顯著特徵,催生人們在 Linux 中大量使用指令碼來編寫程式,並不僅僅是因為指令碼對人直觀、容易修改這種顯著特性所決定的。另外一個主要的原因就是 Linux 所支援的指令碼語言種類十分豐富。

所有類 Unix 系統所必備的 shell,其本身就是一個強大的指令碼直譯器。所以從 shell 誕生的那一天起,shell 就是那些不懂 C 語言,又必須在 Unix 上編寫程式的使用者們的首選工具。

這就給了人們一種新的選擇,使用 shell 程式設計不用去理會讓人頭暈的指標;shell 程式可以直接利用系統命令來完成一些需要用大量 C 程式碼的功能;shell 程式設計不用去理會資料型別,不用考慮煩人的數值和字元資料的轉換問題;shell 程式同樣提供順序、選擇分支和迴圈這三種能夠構建任意演算法的基礎設施。因此,shell 很快就能夠被非專業使用者所接受、掌握,並編寫出非常實用的程式。

隨著時間的推移,這些非專業使用者想往更高的方向發展,遇到了一些 shell 處理起來會很“蹩腳”的問題,比如分析文字和修改文字(別忘了“萬般皆文字”)。這個時候他們會發現 有 awk 和 sed。也只需要寫幾行指令碼就能將這些問題處理得很好。而且它們也跟 shell 配合得天衣無縫。或許這個時候會覺得加入了 awk 和 sed 的 shell 指令碼有些難看,不過沒關係,還有 Perl 和 TCL。Perl 天生就是為處理文字而存在的,TCL 也不含糊。

如果覺得這些語言都太老氣了,有些過時了,不要緊,還有 Python、Ruby 等這些現代指令碼語言,它們除了不能寫作業系統核心之外,幾乎什麼都能幹,而且還是物件導向的。

不管怎樣,在 Linux 下能夠選擇的指令碼語言都是極其豐富的。它們最大的特點就是簡單、好學且資料豐富。簡單就意味著容易維護,好學就容易吸參照戶,資料豐富就不會在解決 bug 上出現障礙。即便是專業的程式設計師,也會因為這些特點而特別偏好指令碼語言,導致的一個結果就是指令碼程式在 Linux 中的大爆發。

有讀者可能會問,為什麼不選擇 C 語言呢?

C語言並不是最佳選擇

C語言是 Unix 的母語,這是毋庸罝疑的。前面也說過,正是因為有了C語言,才使得 Unix 有了今天的成就。但為什麼在 Linux 中有這麼多程式,甚至是關鍵程式,不用C語言編寫呢?

指令碼程式由於是解釋執行的,在執行效率上自然是會有很大損失的。並且大家都知道,C語言所編寫的程式又是以效率著稱的。但是C語言是一種編譯型語言,要想讓C語言的程式能夠執行,必須經過編譯和連結這兩個步驟。

要知道,能夠將由幾十個原始碼檔案構成的C語言程式,有條不紊地編譯完成並能最終連結成一個可執行程式,本身就是一件費時又費力的事情,如果一旦程式有問題,還必須使用專門的偵錯工具一點點地去跟蹤判斷,修正之後再重複那些複雜的編譯和連結步驟,這又是一個極需技巧的事情。積累並掌握技巧又是一件費時又費力的事情。

在早些年,計算機效能不佳的時候,這些付出或許是值得的。但是放到現在,處理器的速度至少快了幾千倍,記憶體大了幾千倍,硬碟甚至大了幾萬倍,而價格卻更低了。從經濟角度分析,機器的時間成本早己遠遠低於人的時間成本了。那麼C語言在機器效率上的優勢根本沒有任何意義。指令碼程式能夠給人節省下來的時間成本,則更具經濟效益。

要論機器效率,組合語言比C語言要好上幾十倍,但是目前還有誰在用組合語言程式設計呢?

C語言在設計的時候,最主要的一個目標就是能夠讓程式設計師自己處理記憶體管理的問題。這使得C語言很強大但又太過於靈活,導致了很多陷阱的出現。稍微一不注意,程式中就會存在難以發覺的 Bug,甚至是嚴重的安全漏洞。程式設計師們大多是要以時間或失敗為代價去積累經驗,才能盡量避免這些問題的發生。而且效率在大多數應用中根本就不是問題,首要的是正確。指令碼程式的簡單和直觀正是正確的起點,C語言的靈活卻是錯誤的根源。

但是,C語言並不是一無是處,也是 Unix 的精華。C語言作為通用程式設計語言是所向無敵的。C語言本身也非常簡潔和緊湊,資料豐富且容易學習。C語言之後的少數語言設計,為了不被C語言所吞併,不得不進行大的改動,比如引進垃圾回收機制等,以和C語言能夠在功能上保持足夠距離。也正是因為這樣,C語言始終沒有消失,只是它的光輝在 Linux 中稍稍地被指令碼程式所遮擋了一下。

指令碼語言也有不足

雖然效率並不是指令碼程式的缺點,但是種類過於豐富卻是一個極大不足。編寫一個複雜的應用,往往很難使用一種指令碼語言套件桿到底,因為指令碼語言都有自己適用的場景,為了能夠快速有效地完成某個應用,就需揚長避短,利用多種指令碼語言混合程式設計。

多指令碼語言的混合程式設計是一種知識密集型的程式設計方法,但不是編碼密集型的(這是能夠被普遍接受的原因)。為了能夠良好地使用這種方法,就要求程式設計師不僅僅要具備相當數量的多種語言知識,還必須具備能夠判斷這些語言的適用場景、以及如何將它們有效地組合在一起的經驗。

實際上,混合程式設計並不是指令碼語言的專利,任何程式語言都行,只要你能找準那些語言的特點。比如筆者就曾經使用過 Basic 和 C 進行混合程式設計,去完成一個 DOS 版萬年歷程式。為了支援滑鼠點選操作,用 C 完成了滑鼠中斷的處理。餘下的部分都用 Basic 來完成。

在 Linux 中大量應用指令碼程式的場景,好多都是這種混合程式設計的典範。比如 Linux 的啟動過程,主程式 init 是用 C 語言寫的,具體到啟動流程的各個環節則是 shell 指令碼程式。