這不是我想要的shell

世上本沒有shell,命令多了,也就成了shell。

——Pope

要說shell,就不能不談起Unix的Keep It Simple Stupid理念。Unix社區崇尚簡潔、專注,鼓勵一個工具只做一件事,做好這件事。當你需要完成一項複雜的工作時,分解,細化,再交給相應的小工具去執行。這樣就需要提供一種機制把各個工具連接起來。所以,在我開來,學習shell重在掌握任務分解和命令間的互聯。

當然,你還會發現,shell的用法很奇怪,畢竟它是個很古老的東西了,包容各種歷史遺留也是無奈之選。

 


 

其實,本文是一篇Learning the Bash Shell (3rd)的讀書筆記。

shell是一種用戶與類Unix操作系統之間以文字方式進行交互的媒介。還有其他的交互形式,比如圖形界面。

shell命令一般是祈使句 如:去 給老子 拿瓶水來。

shell命令先是一個動詞,緊接着若干副詞(option)和名詞(argument)(也可能沒有)。

操作的對象大多是文件(Unix裏幾乎所有東西都是文件)。文件分爲普通文件(文本),可執行文件和目錄。

目錄可以包括若干子目錄,包括本目錄(.)和父目錄(..)。目錄被組織成樹形結構,用路徑來標示。

可以明確指定shell操作的對象的絕對路徑,如果不說就默認是在當前目錄下。當前目錄雖然被稱爲目錄卻沒有物理對應,是一個概念上的浮動地址,可以通過pwd(print the working directory,你看多怪的名字,不知道的還以爲是password呢)命令來查看,還可以用cd命令(change the working directory)來修改。此外還有主目錄(~)和操作記錄中的上一目錄(-)。

shell的操作對象可以是具體名詞(如某個文件名),也可以是抽象名詞(用來描述某一特徵,需要用到通配符(wildcard))。

通配符有三種基本模式:

匹配任一字符、匹配任意字符、匹配字符集。

!, *, […], [!…], {…}

Unix將I/O設備統一看作文件,並把I/O看作處理任意長的字節流。

Unix把I/O流簡化爲標準輸入輸入(standard input)、標準輸出(standard output)和標準錯誤輸出(standard error output)三個端口。輸入流默認爲鍵盤,輸出和錯誤流默認爲顯示屏。如果需要,可以將這些端口映射到其他設備或文件上(重定向)。

Unix下的小工具往往只專注於某項處理。數據被不同的小工具一道道篩選、加工,最後得到想要的結果。正是因爲專注,讓它們更容易組合起來構建複雜的工具。

  • 奇怪的命名

雖然shell命令的命名也不是無章可循,但總歸是很奇怪的。以下是幾個常用的工具:

但比起那些預設變量,這還算好的。

比如位置參數,人家名字直接叫1, 2, ⋯⋯要想取到它們的值,前面加$($var其實是${var}的簡寫),即$1, $2, ⋯⋯。還有0(腳本文件的名字)、*(所有位置參數構成的字符串,用IFS(Internal Field Separator)的首字符作分隔符)、@("$1" "$2" ... "$N")、#(位置參數的個數)⋯⋯

不帶參數的cat就像

while true { echo input }

  • 有管道還不夠嗎

cat是用來解釋重定向的絕佳例子: cat < file1 > file2 等價於cp file1 file2。command < filename,表示command從某個文件而不是標準輸入拿到數據; command > filename,表示command將數據輸出到某個文件而不是標準輸出。

我覺得這個設計很好,但記法太屎。我寧願多敲幾個字,把從哪輸入,輸出到哪明確清楚了(比如: file1 > cat > file2)而不是把<和>作爲前綴貼到操作對象前面。

除了重定向輸出到某個文件,你還可以把輸出作爲其他命令的輸入直接導入(管道pipe,用|表示。這個記法也很屎,體現不出方向來,雖然知道是從左到右。爲什麼不能和重定向統一起來呢?!比如,$ cut -d: -f1 < /etc/passwd | sort | lp 寫出 /etc/passwd > cut -d: -f1 > sort > lp 的形式。可能是於當初把命令設計成前綴形式有關吧,如果當初設計成中綴形式並把命令也看成文件(命令本來就是內存裏的一個文件),也許會方便很多)。

  • 文本編輯原來這麼陽春白雪

bash的emacs-mode對文本編輯提供很精細的控制:對字符的操作(^B, ^F, ^D, )、對單詞的操作(⎋B, ⎋F, ⎋D, ⎋)、對整句的操作(^A, ^E, ^K, ^Y(找回前一次被刪掉的東西)),甚至是對歷史記錄的操作(^P(相當於), ^N(相當於), ^R)。

但emacs-mode最強大的功能還得是文本補全()。當你敲入幾個字符並按下,可能會出現四種情況:

  • 沒有以這些字符開頭的詞,shell會小叫一聲beep;
  • 找到一個唯一的匹配,shell會補全它,並附帶一個空格方便你敲其他字符;
  • 如果找到一個唯一匹配的目錄,shell會補全文件名,並附上一個斜槓(/);
  • 如果有多個匹配可能,shell會補上這些可能匹配的最長前綴。

如果你想知道都有哪些可能的選擇,敲兩下(或者按⎋-?,它會顯示所有可能選擇)。

shell提供了很多快捷鍵來補全不同類型的名字(比如,-/ 專門補文件名,⎋-~專補用戶名,⎋-!專補命令名⋯⋯)

既然提到了emacs-mode,不用想也能知道肯定有個vi-mode。

vi最著名的就是用h, j, k, l作爲←↓↑→。這得追溯到當初Bill Joy用ADM-3a開發vi,這種機器的h, j, k, l鍵上分別有四個←↓↑→小箭頭,所以後面你知道的。

vi-mode和vi一樣,也有兩種模式(輸入模式和控制模式)。在輸入模式下,你可以鍵入、刪除(⌫, ^W)。按下可以進入控制模式。這個模式下,可以用單鍵來控制移動(h, l, b, w, 0, $)(很喜歡這種單鍵控制的方式,其實很多軟件都可以借鑑。如果你不用編輯文字,幹嘛要用control、shift這類的輔助鍵。這就好像有名字空間你不用,非得都搞成全家變量一樣)。你不光可以移動一個字符(h, l)、一個單詞(b, w),甚至還可以移動到某個特定的字符上(F-×, f-×,×表示任意字符)按下i(或a, I(等效於0i), A (等效於$a), R (與r略有不同,r只替換一個字符而且不會進入輸入模式,即你不能再鍵入其他字符))切回輸入模式。控制模式下還有個不得不提的命令——撤銷(u, undo)。另外“.”也很有用,它反覆執行上一次的操作。

vi也有文本補全,不過不是,而是進入控制模式按反斜槓(/)。

一直覺得vi很有意思,設計很精巧,像一門微型語言。比如刪除操作,按下d(delete)之後不會有什麼變化,還要指定刪除的方向(向前還是向後)和長度(字符還是單詞還是整句,是當前光標位置到行首還是到行尾)。這不是v. + adv. + n. 結構麼!而且一個動作可以有多個說法(比如刪除整句,可以用dd,也可以用0d$)。讓你感覺它不是一堆互不相干的快捷鍵,而是一句意義明確的指示。

命令行工具一個總的印象就是太費腦子,這裏指的是要記的東西太多,不如GUI直觀。但好處就在於它能控制得很精細,不像GUI,界面設計成什麼樣就只有哪些功能。

  • 到底0是真是假

在流程控制中,shell用返回狀態作爲邏輯判斷的結果。這樣,0(在[...]中)就表示真,其他返回碼均表示假。但在算術表達式($((...))或$[...])中,0卻是假,1纔是真。而算術式的另一種形式((...))卻用0作爲真,1爲假-_-!!

  • 醜陋的語法

如果說if...then...fi是bash的特色的話,bash的case這是巨醜陋:

 

你就不能正常一點?!至少對稱一點,別弄出半邊括號來好不。

還有,按理說shell的語法是不依賴空格或是換行的。可你看看賦值語句,多了空格它不幹,再看看[ ... ]少了空格它也不幹。而在for, while, select裏do如果新起一行就不用打分號。

shell語法看似統一,其實很混亂。這貨不是一門語言,而是一堆微型語言,雖然每個都很簡單,但要想全都記住卻有不小難度。

P.S. Learning the bash Shell似乎沒有繼承Learning系列簡單明瞭的衣鉢,看似很全,其實毫無系統。示例晦澀,講解不清,比如對getopts裏OPTSTRING(Mac下好像是這個,書中是OPTARG)的說明,OPTSTRING是一個由option及其所帶參數構成的一個序列,而OPTIND是OPTSTRING裏下一個要處理的option的下標(不敢舉例,怕自己理解有誤)。

 

  • 我想要的shell:

首先互聯功能必須很強大;

靈活的語法(不依賴空格神馬的,不限定格式);

統一的語法;

⋯⋯

(cont.)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章