歡迎您光臨本站 註冊首頁

為什麼要在 Emacs 裡面使用 Shell?

←手機掃碼閱讀     火星人 @ 2014-03-12 , reply:0
  
Shell 是 Unix 系統管理員生活的一部分。早期的 Shell 直接運行在各種各樣的 Terminal 裡面,隨著時間的推移,我們有了 X Window。大量的 Shell 開始運行 Xterm 裡面。對於筆者來說呢,更加喜歡讓 Shell 運行在 Emacs 裡面。將 Shell 運行在 Emacs 裡面與運行在 Xterm 裡面到底有什麼不同呢?相信我,將 Shell 從 Xterm 裡面搬出來絕對不是一個賺取眼球,標新立異的決定。這篇文章介紹了將 Shell 從 Xterm 裡面搬到 Emacs 裡面帶來的各種改變。這篇文章適合 Emacs 的初級與中級用戶,需要讀者具有一定的 Shell 使用經驗。最好具有較長時間的 Shell 使用經歷,以更加充分的體會到 Emacs 帶來的變化。文章中的內容謹代表作者的個人觀點。不代表 IBM 的任何官方觀點。所附代碼僅在運行於 Cygwin 環境下的 GNU Emacs 當中測試通過。

為什麼要在 Emacs 裡面使用 Shell?

為什麼要使用 Shell? 我不也太清楚,但是,在這樣一個 Windows 橫行的年代里,總是有著無數的 Unix 管理員在遍布全球的各個角落裡默默奉獻,保證著我們的世界繼續運行。那麼,為什麼要在 Emacs 裡面使用 Shell? 在 Emacs 裡面使用 Shell 和在其他地方相比,比如說 xterm,甚至是 Windows 的命令行窗口裡面使用 Shell 有什麼不一樣呢?這個問題,我個人認為不是一個技術問題,而是一個生活舒適度的問題。之所以選擇在 Emacs 裡面使用 Shell,就是因為在 Emacs 裡面會使你的生活的更加舒適。

生活舒適度的問題

信不信由你,人們總是希望生活的更加舒適。那麼,把 Shell 搬到 Emacs 環境里究竟能夠帶來那些生活舒適度的提高呢?在回答這個問題之前,讓我們先進行一個簡單的熱身運動。

大家都知道,現代的 Bourn Again Shell(bash) 要比古老的 Bourn Shell(sh) 用起來舒服多了。因為 Bourn Again Shell(Bash) 引入了很多非常舒服的新的特性。比如說歷史 (History) 功能,比如說命令行編輯功能。這些都是大家非常喜歡的功能。不是嗎?但是,問題是,如果你想在 Shell 的世界中生活的更加舒適,就會發現很多時候他都不能真正的讓你滿意。

通常來說,但凡有過在古老的 Bourn Shell(sh) 或者某些缺乏歷史功能的 Korn Shell(ksh) 環境下的使用經驗的人來說,Bourn Again Shell(bash) 的歷史 (History) 功能都是一個非常可愛的功能。它提供了很多很好的重複歷史命令的有趣方式,我們可以使用 ! 命令去全部或者部分的引用上一條剛剛執行過的命令,我們可以使用 ^^ 命令簡單編輯上一條命令並且生成一條新的命令,我們可以也使用向上的箭頭鍵去回朔查找許久以前曾經執行過的命令,然後使用命令行編輯功能隨心所欲的編輯這條命令。但是這裡面有一個非常隱蔽的問題:所有這一切,都是集中在“命令行”這個概念中的。說白了,各種各樣的不同的 Shell 版本提供給我們的就是一個命令解釋器,外加一個命令行編輯環境。

這樣有什麼不對嗎?對,但是慾望總是沒有止境的。為什麼不能夠全屏幕編輯呢?

讓我們來看看一個很簡單的例子。我有一個 Omegamon XE for Messaging 的安裝。這是 IBM Tivoli 旗下的一款監控產品。通過與 IBM Tivoli Monitor 產品的集成,實現對 WebSphere MQ,WebSphere Message Broker 產品的運行監控,以及對 WebSphere MQ 產品的修改與配置。由於某種需要,現在我需要將一個 MQ Monitoring agent 停止下來。我不想將那個漫長的命令從頭到尾輸入一遍,因為我知道我可以使用 history | grep ‘itmcmd agent’ 命令從已經存在的命令歷史裡面找到曾經啟動這個 agent 的命令,然後我所要做的,僅僅只是在原來命令的基礎上將 start 參數修改為 stop 就可以了。但是……事實上 Shell 並沒有給我一個修改這條命令的機會。


圖 1

事實上我必須先用滑鼠將屏幕上的第 437 條命令複製到剪貼板裡面,然後把它粘貼到我的命令提示符後面,之後再使用 bash 的命令行編輯功能,將那個可愛的 start 參數修改為 stop。說實話,我的某些同事每天就是這樣做的,另一些則是乾脆將停止命令從頭到尾再輸入一遍。回答我一個問題,你有沒有曾經也像我一樣,在看到 history | grep 的輸出的時候,曾經夢想過,能夠直接在第 437 條命令上面改幾個字母,然後就直接敲下回車呢?現在你可以了——在 Emacs 裡面。在 Emacs 裡面,你可以將你的 Shell“擴展”到全屏幕編輯。

讓我們重新查找剛才提到的那條命令。這一次,我們不用 history | grep 的方法。是的,這個方法仍然管用。但是既然是在 Emacs 裡面,我希望介紹一些更加 Emacs 化的使用方法,我們使用 Ctrl-r 組合鍵。如果你已經使用過 Emacs,但是從來還沒有在 Emacs 裡面看到過 shell 提示符的話,不妨跟我一起來,在 Emacs 當中輸入 ESC-x 組合鍵,然後在提示緩衝區裡面輸入 shell(全部字母小寫)回車,然後你就會在 Emacs 的編輯緩衝區裡面看到熟悉的 shell 提示符了。在接下來的例子裡面我們假設你已經執行過啟動 MQ Monitoring agent 的那條命令了,大約是在 4 個小時前或者別的什麼時候。現在要做的,就是把這個特定的 agent 停止下來。跟我一起在 Emacs 裡面輸入 Ctrl-r 組合鍵,這個時候你會在提示緩衝區裡面看到 I-search backward: 這樣的字樣。沒錯,這個組合鍵對應的就是 Emacs 當中的回朔查找的功能。我們所要做的,就是像通常在一篇文檔裡面查找一個句子一樣,在回朔查找提示符的後面輸入 itmcmd agent,Emacs 非常迅速的就把游標定位到了曾經執行過的 bin/itmcmd agent –o QM_106 start mq 這一行命令上面。就在這裡,讓我們把 start 修改為 stop,然後,直接就在這裡按下回車鍵——修改以後的命令會自動的被複制到正確的 shell 提示符後面並且被執行。無窮無盡的拷貝與粘貼終於被終結了。

更理想的情況下,如果你想修改的命令還仍然顯示在你當前的屏幕範圍內(這在我的工作當中覆蓋了大多數情況),甚至可以省略使用 Ctrl-r 組合進進行查找的過程,直接移動游標到你需要的命令,修改,或者回車就是了。


圖 2





技術性擊倒

上面提到的還只是一些很簡單的事情。或者說是一些使用習慣方面的事情。每個人都有自己的生活習慣。我就見過一些人,他們甚至很少使用 History 功能。他們執行每一條命令都會從頭到尾輸入進去。如果中間有什麼東西寫錯了,他們會停下來再輸一遍。我只能承認我沒有他們那麼勤奮了。

下面我們要談論一些真正技術性的問題。一個實際工作中的案例問題。我在上面提到過,我的日常工作的一部分就是使用 Omegamon XE for Messaging 產品,既然他是監控 WebSphere MQ 的一款產品,那麼同樣意味著我也會頻繁使用 WebSphere MQ 產品。一天,我需要處理這樣一個問題,我有 11 個處於停止狀態的隊列管理器(Queue Manager),他們全部需要啟動起來。


圖 3

通常在 shell 環境下,會有兩個選擇。第一個選擇,一條一條的編寫 11 條 strmqm 命令,將所有這些隊列管理器一個一個地啟動起來。第二個選擇,我可以使用 SED 實用工具編寫一些小腳本,利用 dspmq 命令產生的輸出內容來生成這 11 條命令,並把它們輸出到一個文件裡面。是的,在使用 Emacs 之前我是使用這第二種方法的。現在,在 Emacs 裡面,我可以使用第三種選擇,一種所見即所得的選擇。

Emacs 不僅提供了全屏幕編輯的全套功能,而且還提供了超越傳統編輯方式的矩形編輯功能。使用這個功能,你可以把屏幕上的任何內容作為一個矩形塊進行剪切或粘貼操作。現在跟我一起,把游標定位到 QM_100 的字母 Q 的位置,並輸入 Ctrl-@ 組合鍵設置起始的標記位。接下來把游標移動到 QM_110 的數字 0 的後面,輸入 Ctrl-x r k 組合鍵(對,一共是 4 個按鍵。但是仍然非常容易記憶,只要記住中間的 r 是 rectangle 的縮寫就行了,所有的矩形編輯命令都會有這個 r 字母加入)。看看屏幕上面發生了什麼?是的,所有的隊列管理器的名字全都不見了。他們被作為一個矩形塊剪切進了 Emacs 的刪除環(king-ring)當中。


圖 4

接下來,讓我們將這些剪切下來的隊列管理器的名字粘貼到一個用來啟動這些隊列管理器的腳本文件中去。在這裡我們使用最簡單的 here 文檔的功能   cat <<eof >strmqm.sh,創建一個空白的腳本文件。然後,在 Emacs 當中輸入 Ctrl-x r y 組合鍵,這個組合鍵的作用是將刪除環裡面的矩形文本塊作為一個矩形,粘貼回去。下面的截圖就是你在屏幕上所看到的結果。


圖 5

沒錯,你注意到了,我們真正需要的並不是隊列管理器的名字列表,而是一連串 strmqm QM_100 這樣的 shell 命令。沒問題,接下來讓我們繼續使用 Emacs 的矩形編輯功能來完成這些命令。再次把游標移動到 QM_100 的字母 Q 的位置,輸入 Ctrl-@ 組合鍵設置標誌,然後移動游標到 QM_110 的字母 Q 的位置,輸入 Ctrl-x r p 組合鍵。這個組合鍵的作用是在選定的位置前面填充一個矩形區域,填充的內容在提示緩衝區裡面輸入。現在在提示緩衝區裡面 string insert rectangle: 的提示符後面輸入 strmqm 加空格。下面的截圖就是這個時候你在屏幕上所看到的結果。


圖 6

這裡面有一個小秘密需要提示一下,因為在 here 文檔的提示符下事實上是不可能進行這樣的修改的。但是,直到目前為止 Emacs 還沒有將編輯緩衝區里的內容傳遞給 here 文檔提示符。以上所做的一切還都僅僅發生在 Emacs 內部。

現在輸入一個回車鍵,已經編輯好的 11 條 strmqm QM_1xx 命令就已經傳遞給 here 文檔提示符了。最後輸入 eof 結束文檔編輯。一個簡單清晰的隊列管理器啟動腳本就已經完成了。只要在 shell 提示符下通過 bash strmqm.sh 命令運行它,就可以啟動所有這 11 個隊列管理器了。


圖 7





一些有趣的小技巧

第一個小技巧:在 Emacs 裡面同時打開多個 shell 會話

這聽起來應該是理所當然的,既然 Emacs 可以同時運行多個窗口……但是即使是在我買的那本關於 Emacs 的書裡面也沒有提到如何能夠真的打開多個 shell 會話。讓我們試試看,輸入 ESC-x shell 啟動 shell 會話,在 shell 提示符下執行 cd /tmp 命令,然後輸入 Ctrl-x 2 組合鍵將屏幕切分成上下兩個窗口,然後再次輸入 ESC-x shell 啟動 shell 會話,你會發現 Emacs 不為所動。在上下兩個窗口裡面仍然還是 cd /tmp 命令執行后的結果,也就是說仍然還是原先的那個 shell 會話。說起來 O’reilly 公司應該退還我一部分書錢,因為他們沒有將我想知道的問題講明白。後來我終於發現了解決的辦法。只要我將當前的 shell 會話所在的緩衝區重新命名,然後就可以再次啟動一個新的 shell 會話。讓我們來試試看。輸入 ESC-x rename-buffer,在 Rename buffer (to new name): 提示符后輸入一個新名字,例如 *shellA*。這個時候你會看到上下兩個窗口中的緩衝區都被重命名為 *shellA* 了,是的,他們顯示的是同一個緩衝區。現在在任意一個窗口裡面輸入 ESC-x shell 啟動 shell 會話,這時你會看到一個新的 *shell* 緩衝區創建了出來,並且有了一個新的 shell 提示符。現在讓我們在這個新的 shell 提示符下執行 cd / 命令,你會發現,緩衝區 *shellA* 當中的 shell 會話並沒有受到新的命令的影響,因為他們是兩個不同的 shell 會話。重複同樣的方法,可以得到更多的 shell 會話,滿足你的使用需要。


圖 8

第二個小技巧:history | grep 的輸出結果可以直接輸入回車使用

讓我們再回到當初提到過的第 437 條命令。如果需要再次運行這條命令,你只需要把游標定位到這條命令所在行,刪除掉命令前面的 437 這個號碼,然後直接輸入回車,Emacs 會提你把它複製到正確的 shell 提示符后並且啟動執行。似乎這個方式描述起來並沒有 shell 本身提供的 !437 命令來的迅速,但是他畢竟提供了一個更加直觀的操作可能。另外,如果希望在運行之前對於這條命令進行一些修改,我指的是刪除,例如刪除掉某個參數,或者單詞,你也盡可以盡情刪除,然後在最終的刪除結果上面輸入回車,告訴 Emacs 去複製運行就是了。這個狀況目前似乎還沒有發現 ! 命令有可以搞定的可能。另外,如果希望在運行之前對於這條命令進行一些添加性質的修改,例如添加一些新的參數,或者把第 437 條命令中的 start 單詞刪掉,添加上 stop 單詞,在目前的 Emacs 裡面還沒有提供直接的支持。但是我們可以通過增加一步操作來實現它。第一步,讓我們首先把游標定位到第 437 條命令的位置,然後輸入回車,這個時候命令不會成功運行,一行提示信息 bash: 437: command not found 會顯示在你的 shell 提示符下面。沒關係,讓我們稍微修改一下剛剛運行失敗的這條命令。重新定位游標到剛剛執行過的這條命令所在的行,然後把命令中的 start 參數修改為 stop 參數,並且刪除命令前面的 437 這個號碼,再次輸入回車,修改之後的命令就會成功運行了。這裡增加的第一步操作實際上代替了通常情況下需要的拷貝粘貼操作,拷貝和粘貼事實上是交由 Emacs 替我們實現了。


圖 9

第三個小技巧,整理你的屏幕

在前面講到生活舒適度話題的時候,我們曾經提到過在 Emacs 裡面使用 shell 的一個很常用的方法就是在屏幕上已有的命令上面稍作修改,或者直接回車來複用已經執行過的歷史命令。但是如果你所感興趣的命令因為種種原因已經不在當前屏幕顯示的範圍之內了,除了不斷的通過使用 Ctrl-r 組合鍵進行回朔查找以外,還有沒有什麼更方便的辦法呢?答案是有,而且還有不止一種選擇。如果在你的當前提示符和你感興趣的命令之間僅僅只是由於時間的關係隔著太多無關的命令記錄,那麼你盡可以簡單的將這些多餘的內容刪除掉就行了。不要擔心,這些內容僅僅只是在 *shell* 編輯緩衝區里的普通文本內容而已,將他們刪除和將一篇文檔裡面的文本刪除沒有什麼區別。如果這些中間的命令記錄並不是完全無用了,僅僅只是在現在這段時間不感興趣的話,你還可以選擇使用 hide-region-hide 命令將這些中間的命令記錄作為一個文本塊隱藏起來。這些隱藏起來的內容僅僅只是不再在屏幕上顯示而已,他們仍然存留在原有的編輯緩衝區裡面,以後當你再次需要他們的時候,可是使用 hide-region-unhide 命令再把他們重新顯示出來。還有一種情況就是僅僅因為你剛剛執行了一個產生大量輸出的命令而已,比如說使用 cat 命令書出了一個較大的日誌文件,從而導致你感興趣的歷史命令被日誌文件的內容衝出了當前屏幕顯示範圍,這個時候只需要輸入 Ctrl-c Ctrl-o 組合鍵,將剛剛執行過的那條命令的屏幕輸出清除掉就是了。通常我在每次 cat 日誌文件或者列出一個含有較多文件的目錄之後都會使用 Ctrl-c Ctrl-o 組合鍵來清理一下我的屏幕。

最後我們再來說一個非常有用的小技巧,使用縮寫詞

Emacs 的強大的縮寫詞(abbrev)功能,這些縮寫詞功能同樣可以應用到 Emacs 當中的 shell 環境里來。通常在使用 Emacs 便寫文章的時候,我們會使用縮寫詞功能來減少頻繁輸入冗長的單詞或者語句時候的擊鍵次數,或者使用縮寫詞功能避免一些可能的拼寫錯誤。當我們在 Emacs 裡面使用 shell 的時候,我們同樣可以使用縮寫詞功能來縮寫一些冗長的命令,路徑,和避免一些常見的輸入錯誤,這樣在每天的工作當中會為我節省大量的擊鍵次數。有些讀者看到這裡一定會想到 shell 本身提供的別名(Alias)功能也能完成命令的縮寫,但是,別名功能有很多局限性。首先,別名功能縮寫的命令只能通過別名單獨引用,無法和其他 shell 命令一同組合運行。例如我可以定義一個別名來進入常用的 IBM Tivoli Monitoring 產品的安裝目錄,alias cditm=’cd /opt/IBM/ITM’, 然後輸入 cditm 執行這條命令來進入 IBM Tivoli Monitoring 產品的安裝目錄。但是我不能夠定義一個別名來縮寫這個目錄本身,alias itmm=’/opt/IBM/ITM’, 然後輸入 cd itmm 執行這條命令來進入這個目錄,或者輸入 itmm/bin/cinfo –r 來直接引用該目錄下的可執行文件。還有一個問題,通過別名定義的命令縮寫只能夠直接引用執行,沒有可能對於很多相似的命令定義通用的一個“別名模板”,然後每次使用的時候引用模板,然後稍作修改之後運行。例如啟動 Omegamon XE for Messaging 產品中的 MQ Monitoring agent 的常用命令,bin/itmcmd agent –o QM_101 start mq。我可以把它定義為一個別名,alias strkmq_QM101=’bin/itmcmd agent –o QM_101 start mq’, 但是事實上每次在啟動一個 MQ Monitoring Agent 的時候,其中的隊列管理器(Queue Manager)的名稱都是不同的,我不可能為每一個隊列管理器都定義一個單獨的別名。況且還有其他的 agent,這樣的話別名的數量將會相當可觀。

Emacs 的縮寫詞功能在這裡提供了兩步走的設計方法,避免了以上提到的所有問題。讓我們還是以上面提到的目錄和命令的兩個例子來了解一下在 Emacs 裡面如何使用縮寫詞功能來解決這些問題。首先是常用目錄的縮寫詞定義。在 shell 提示符下輸入 cd /opt/IBM/ITM 命令,不用專門為了縮寫詞去輸入這條命令,就在你日常工作當中需要的時候輸入就是了,只是記得先不要輸入回車,然後,讓我們數一數究竟有多少個單詞將被包含在這個縮寫詞定義當中,在這裡是 3 個單詞,分別是 opt, IBM 和 ITM,現在輸入 Ctrl-u 3 Ctrl-x a l 組合鍵,稍微有點兒複雜,我在這裡簡單解釋一下,Ctrl-u 在 Emacs 裡面是一個非常有用的組合鍵,它代表一個通用前綴,通常在後面跟一個數字參數,表示隨後輸入的命令將被重複多少單元,在這個例子里是 3 個單元,後面的 Ctrl-x a l 組合鍵代表在當前主模式下添加縮寫詞定義(add-mode-abbrev)命令,其中的 l 代表將該縮寫詞的定義添加到當前的主模式中,如果希望這個縮寫詞定義在任何主模式下都可以使用,那麼將組合鍵中的 l 替換成 g,即 Ctrl-x a g, 代表 add-global-abbrev 命令。然後,在提示緩衝區確認定義無誤之後,輸入希望的縮寫單詞,itmm,即可完成縮寫詞定義。


圖 10

接下來我們來使用這個縮寫詞。例如我們需要進入到 IBM Tivoli Monitoring 產品的安裝目錄,在 shell 提示符下輸入 cd /itmm 加空格,你會發現 Emacs 立刻將這條命令擴展成為 cd /opt/IBM/ITM 顯示在剛才的輸入位置。現在我們要執行 IBM Tivoli Monitoring 產品目錄下的 cinfo 可執行程序,在 shell 提示符下輸入 /itmm 加空格,Emacs 立刻將這條命令擴展成為 /opt/IBM/ITM 顯示在剛才的輸入位置,然後我們只需要輸入一個退格鍵刪掉剛才那個空格,繼續在後面輸入 bin/cinfo –r 就可以了。事實上由於在我的工作當中 , 對於 cinfo 命令來說,-r 這個參數非常常用,所以在我的 Emacs 里,bin/cinfo –r 已經被縮寫成了 rcinfo 這個縮寫詞了。那麼在我的 Emacs 裡面我只需要輸入 /itmm 空格鍵,退格鍵和 rcinfo 就可以實現完整的 /opt/IBM/ITM/bin/cinfo –r 這條命令了。

再下來我們來看看啟動 MQ Monitoring Agent 的命令的縮寫詞。在 shell 提示符下輸入 bin/itmcmd agent –o start mq 命令,注意,在–o 參數後面沒有提供隊列管理器的名字。因為每次執行這條命令的時候,隊列管理器的名字都有可能變化,因此我們不把它定義到縮寫詞內部。輸入 Ctrl-u 6 Ctrl-x a l 組合鍵,然後,在提示緩衝區確認定義無誤之後,輸入希望的縮寫單詞,strkmq 即可完成縮寫詞定義。


圖 11

現在讓我們來使用這個縮寫詞。首先我們需要使用 itmm 這個縮寫詞進入到 IBM Tivoli Monitoring 產品的安裝目錄,然後,在 shell 提示符後面輸入 strkmq 空格,Emacs 立刻將這條命令擴展成為 bin/itmcmd agent –o start mq 顯示在剛才的輸入位置,現在移動游標到–o 參數後面,輸入隊列管理器的名稱並且回車,你就會看到希望的命令得到運行。

發明一個完美無缺的 shell 是一件相當困難的事情,但是將現有的資源整合起來卻會產生很多意想不到的效果。這個世界已經有了很多現成的科技資源,如果我們在這些資源上面進行創新,整合,不僅很多問題都可能迎刃而解,而且還可以為我們自己再節省一些喝茶和咖啡的時間。(責任編輯:A6)



[火星人 ] 為什麼要在 Emacs 裡面使用 Shell?已經有751次圍觀

http://coctec.com/docs/linux/show-post-69059.html