實現一個shell解釋器需要哪些編譯原理方面的知識?


謝邀。先安利一個表達力強、UI 友好的新 POSIX shell:elves/elvish · GitHub。elvish 代碼簡單,適合拿來研究學習。

問題的答案取決於題主想要實現傳統的 POSIX shell 還是另闢蹊徑做一個更像樣的非 POSIX shell 。實現傳統的 POSIX shell 不需要多少「正統」編譯原理的知識。因為 POSIX shell 的解釋過程是若干次文本替換(所謂「expansion」)的過程,而不是通常的先解析成語法樹再執行。好處是沒有任何理論深度,按照 POSIX 標準來就行了。壞處是用正常的技術解析 POSIX shell 反而很麻煩,即使能夠解析,要完全模擬 expansion 的語義也不簡單。個人意見:POSIX shell 歷史包袱太多,而且早已被 zsh 做到了極致,因此既缺少練習的價值也缺少實用的價值,在 2015 年去實現一個新的 POSIX shell 是事倍功半的事。

如果打算實現一個新的非 POSIX shell,那麼不得不會的編譯原理知識就是基本的解析(parsing)技術。不過有點頭疼的是,處理不完整、病態輸入的技術在一般的編譯原理教科書上是不深入探討的,理論方面也比較欠缺。但是這類技術對 tab 補全十分重要,需要自己摸索或者參照現有的實現。

如果追求性能的話,還要學習 VM 和 JIT。但如果只是應付交互使用,直接執行解析出來的 AST、甚至邊解析邊執行也沒問題,反而可能更方便。

shell 和普通的編程語言最大的區別在於,它還要求很多系統編程的知識,主要是進程和 I/O(下面包括終端 I/O)這兩方面。對照 Advanced Programming in Unix Environment 第二版的目錄,第三章文件 I/O、第八章進程式控制制、第九章進程關係、第十章信號、第十四章高級 I/O、第十八章終端 I/O 里的知識幾乎全都能用上。Unix 系統編程坑不少,要想做一個好的 shell,得對系統介面相當了解才行。

最後再安利幾句。如果樓主有志於改進 Unix shell 現狀,不妨看看 elvish 源碼並參與它的設計和實現。如果答主更喜歡語義上更簡單(同時表達力也更弱)的 shell 的話,fish (fish-shell/fish-shell · GitHub) 作為在現代 Unix 生態下流行開的第一個非 POSIX shell,給它作貢獻也是個選擇。不過如果你讀過 fish 和 elvish 的代碼的話,肯定還是會更喜歡 elvish 的。:-P


做 Shell 最麻煩的是設計語言,尤其是當你想給管線加類型的時候。

nagisa-w 就是因為文法不好定一直坑著,現在的 proposal 是粗略承襲 PatEL 的,腳本內部被拆分成命令範疇表達式範疇兩種區域,文法不同。代碼類似這樣:

find -recursive "*.png" | map : =&> [file] [extensionOf file.name]
from ips.txt | lines | foreach [=&> [ip] {ping -n 1 -w 1000 #ip}] | into pingtimes.txt


我最近在讀這本書:Unix/Linux編程實踐教程 (豆瓣)

第8章(進程和程序:編寫命令解釋器 sh)和第9章(可編程的shell、shell變數和環境:編寫自己的shell)就是介紹如何編寫shell解釋器,題主不妨試試看。


可以從下面兩個角度去切入學習shell

1. unix shell

Shells are specialized, domain-specific languages (little languages) that implement a specific use model—in this case, providing an interface to an operating system.

第一個版本的shell sh http://v6shell.org/history/sh.c, 出自Ken Thompson,這是最簡單的版本,跟現在的shell比起來,少很多高級的特性,只是做命令交互的,缺少scripting,後來發展起來的shell都加入了scripting。

實現一個shell (bash)基本的架構如下

如果是想 在Linux Unix基礎上,自己重新學習系統介面編程和shell的實現,那這一套比較適合,有比較清楚的系統API的knowledge,IO,Process,通信。

1. no unix shell

FreeNOS

lordsergioinspa/FreeNOS · GitHub

通過學習現成的simple的OS,抽出其中的shell部分,這還是比較容易的,而且相對於直接學習Linux Shell容易,沒有太多的依賴和系統相關API的學習壓力,可以把精力放在存粹的shell實現的學習上。

我在學校的時候,曾經模仿過FreeNOS的shell,自己學習的寫了一個輕量的跨平台的shell,

alexzhang2015/shell-simulator · GitHub

希望能有所幫助


shell那種混亂的語言,你學了編譯原理只會讓你覺得更難寫了,還不如不要學,硬著頭皮寫出來,說不定更快。


我沒有學過任何編譯原理方面的內容,但是我在學習操作系統課程的時候,實現了一個 `mini shell`

支持以下功能:

  1. 解釋執行命令
  2. 內建命令cd exit
  3. pipe 管道
  4. fg 前台執行命令
  5. bg 後台執行命令
  6. 彩色提示符
  7. Ctrl -Z Ctrl -C 信號處理

項目地址:David-Guo/myshell · GitHub

覺得還不錯的話,歡迎 star

如果你有興趣,完全可以通讀源碼,並自己加入一些新的特性。

希望能給你提供一個思路和 demo 。

以上


先不要管什麼編譯原理和《APUE》的,先試著一行一行地解釋指令。了解程序能夠接收的環境變數(在unix上就只能是純文本),搞清楚各種情況下的輸入輸出重定向(pipe)如何實現,以及如何調用程序(fork/spawn + exec),及各種情況下如何中斷程序(signal)。這樣基本就夠了。

其它的,前後台任務調度,自動補全,歷史記錄,字元串替換,函數定義,以後再慢慢完善吧。不要想得太複雜了。

那些想用shell腳本干一大堆複雜任務又設計不出夠好編程語言的,往往就是給自己挖坑,語言一旦設計好以後就很難改的了,越精細越難改。綁定個小巧的Lua腳本解釋器都比那個強。像shell這種腳本環境能設計出什麼怪物,看bash就知道了,也能從bash的發展看出什麼功能是最被需要的。我建議腳本語法越簡單越好,方便日常使用。fish shell的語法看起來不錯,很值得參考,但沒有提供詞典變數。而scsh就是bash之外的一個奇怪反面例子:有Scheme的各種括弧漂亮走位,不自立腳本語言,也沒有複雜的語法,然並沒有什麼用,至今連個UI體驗都沒設計好,想用好它還得先學好LISP,有多少人會用呀。

說到fish,千萬別被fish的設計理念忽悠了,表面上說是用戶友好,功能各種簡單實用不混亂,實際上一大堆未定義或未入說明文檔的細節都沒做好,在最常用的功能上更有強大到沒人會仔細發現的dirty hack(偽字元串數組),這shell過了十年的發展就這鬼樣子,實在是嚇到我了。牛千萬不要亂吹,語法這東西一定要做好,不要耍花樣,不然根本沒有前途。上面肖同學拋棄fish自己干就是一個警示。

最後回到編譯原理,除非你要搞byte code解析器(將指令化為較為精簡的byte code緩存之以便快速重複調用),或者是更高級的JIT compile啥的,否則你的實際目標就是解釋一下語法樹而已。不用想你也應該先設計好shell的實現再想這個。

最後還有一個忠告:離POSIX越遠越好,當今世界並不是完全依那個標準發展的。尤其是locale那個臭東西,把C語言的標準庫都污染了不少。把那些包袱扔給bash就好。bash已經成為廣大linux的標配了,不要想搶它的地位,就連zsh也不能。


向前兼容的包袱太重。跟編譯原理的關係,倒是不那麼大。

既然你還要來知乎問這個問題,不建議你做。


推薦閱讀:

為什麼WinXP的驅動無法用於Win7、Vista,但是Win7,Vista驅動可用於Win10?
計算機掉電的時候 CPU 真的會中斷嗎?操作系統會進行那些動作?
mac上有必要裝linux嗎?
為何windows系統沒有訂閱模式?
linux下如何測量進程線程context switch花費的時間?

TAG:操作系統 | 編譯原理 | Linux開發 | UNIX環境高級編程書籍 |