天天看點

感歎号:bash 的曆史擴充功能

bash 的曆史擴充(history expansion)又被稱為 bang(!) 指令,曆史擴充是 bash 将曆史指令轉換到可執行指令的過程。bash 下的 history 庫提供了一個與 csh 下曆史擴充類似的曆史擴充功能。曆史擴充中操作曆史指令一般有兩個部分:

首先要從曆史指令中找出相對應的指令,被選擇到的指令我們稱作為event(條目),比如bang bang(!!),就是選擇最後一條指令;

選擇標明行的部分或全部文本以包含到目前行中。要操作的條目(event)bash将其拆分成了words(詞),指令中的words是靠空格來分割的,我們就可以使用修飾符(modifiers)來調整words以符合我們的要求。注意:words并不是英文單詞,而是一個字元序列而已。

先來看兩個指令,你知道第二個指令是什麼意思麼?

<code>cat /tmp/cat.cat.txt</code>

<code>!:0 !*:gs/cat./echo.</code>

<a target="_blank"></a>

條目标志符是一個到曆史清單内一個指令行實體的引用,除非是絕對引用,不然條目的引用是相對曆史清單中目前位置的。

條目标志符

條目标志符說明

<code>!</code>

開始一個曆史替換,除非後面緊跟的是空格,制表符,行結束符,"=","("(當使用内建指令<code>shopt</code>開啟了<code>extglob</code>的shell選項)。

<code>!n</code>

重複曆史中編号為n的指令——曆史編号可以參看<code>history</code>指令.

<code>!-n</code>

執行之前的第n條指令,執行上一條指令可以使用!!或者!-1,執行之前第三條指令:!-3,倒推的清單是<code>history</code>。

<code>!!</code>

執行上一條指令,和ctrl-p,!-1的作用一樣。

<code>!string</code>

執行最近的以string字串開頭的指令。這個指令的意思是重複以!後字串開頭的最後一條指令,比如:!ca将重複以字元ca開頭的最後一條指令,如<code>cat readme</code>,(假設最近一條ca開頭是這個指令,并且readme後緊跟換行符)

<code>!?string[?]</code>

在曆史清單中以目前位置開始向後查找(往回搜尋)包含string字元串的最近一條指令,如果要查找的string字元串後面緊跟換行符,則string後面的這個問号可以省略。例如:!?read?還是會比對<code>cat readme</code>。(同上的環境),如果後面是換行符如:!?readme,則不用輸入結尾的[?]。

<code>^a^b</code>

快速替換,把上一條指令中的a替換成b,并執行替換後的指令。^a^b^類似。注意:這裡隻是替換一個找到的執行個體,相當于:<code>!!:s/a/b</code>。

<code>^abc</code>

删除上一條指令中的abc。

<code>!#</code>

引用目前輸入的所有字串,如:<code>more a !#</code>;這個最終的指令是<code>more a more a</code>。

詞标志符被用來在條目裡面選擇需要的詞。一般用":"分隔條目訓示符和詞訓示符。當詞訓示符是以"^","$","*","-","%"開頭時,也可能會省略":"。詞是從一行的行首開始,第一個詞編号為0.插入到目前行中時,這些詞使用單個空格分隔。

詞标志符

詞标志符說明

<code>0</code>

第0個詞,在很多應用程式中,這就是指令本身。

<code>n</code>

第n個詞

<code>^</code>

第一個參數;也就是第一個詞。

<code>$</code>

最後一個參數。

<code>%</code>

最近"?string?"比對的詞。

<code>x-y</code>

詞的範圍:如果是'0-y'可以簡寫成'-y'.

<code>*</code>

除了第0個以外的所有詞,這個和'1-$'同義,如果條目中隻有一個詞,使用'*'也不會傳回錯誤,僅是傳回一個空字元串而已。

<code>x*</code>

'x-$'的簡寫

<code>x-</code>

和x*類似,都是'x-$'的簡寫,不過需要注意,這個寫法是忽略最後一個詞的。

需要注意的是,在bash下使用詞訓示符的時候,可以沒有條目訓示符,如果沒有使用條目訓示符,則會把前一條指令作為詞訓示符的操作條目。

在可選的詞訓示符之後,你可以添加下面修飾符中的一個或多個,每個修飾符以':'開頭。

修飾符

修飾符說明

<code>h</code>

去掉路徑名的尾部,隻保留頭部。隻移除最後一個'/'後面的内容,可以了解成是路徑名的父目錄。

<code>t</code>

去掉路徑名部件中除尾部之外的所有内容。隻保留最後一個'/'後的内容。

<code>r</code>

去掉尾部這樣格式".suffix"的一個結尾字尾,保留基本名稱。隻删除最後一個點'.'後的内容。

<code>e</code>

僅保留字尾。僅保留最後一個點'.'及點後的内容。

<code>p</code>

列印新的指令但不執行。

<code>q</code>

引用替換的詞,防止進一步替換。(譯注,原文:quote the substituted words, escapin further substitutions.——mitchell chu)。這個引用會直接對引用的指令加上單引号,防止進一步替換。開始這句不知道怎麼翻譯。後來mitchell發現自己的這個翻譯并沒有錯誤,因為我們引用的詞可能是個變量,這時候如果沒有引号,就會引起進一步的替換,而是用此參數就能達到防止這種情況的發生。

<code>x</code>

這個和q一樣,是引用替換的詞,但是這個與q不同的地方在于,q是整體引用,而這個是會将替換的詞使用空格,制表符,換行符來分割成一個個的詞。

<code>s/old/new/</code>

把條目行中找到的第一個old位置的内容替換成new位置的内容,'/'這個分隔符位置可以使用任何其他字元作為分隔符。如果要在old或new位置使用分隔符,需要使用反斜杆'\'來轉義。如果'&amp;'這個字元出現在new位置,将會被替換成old位置的内容,如果要使用'&amp;'請用'\'轉義。最後一個分隔符如果是整行的最後一個字元,則可以省略。

<code>&amp;</code>

重複上次替換。這個是引用最後一次的s/old/new/内容。

<code>g</code>

見下,與a相同

<code>a</code>

使替換在整個條目中進行,和's'一起使用,例如:<code>!!:gs/old/new/</code>,或者和'&amp;'一起使用。

對條目中的每一個詞都執行一次其後的's'修飾符。這個方法在bash 4.1.2下測試并不靠譜。

<code>test $eee /tmp/test.log</code>

<code>echo !test:gs/t/a/;</code>

<code>## 這個傳回的test被替換兩次</code>

<code>## 但後面的參數僅替換一次</code>

是以mitchell在想,是不是僅對參數執行一次,而對指令(第0個詞)進行全局替換。但另外一個測試,反駁了這個觀點:

<code>aaaaaaaaaaa $aaaaaaa /tmp/aaaaaaaaaaaaa.log</code>

<code>echo !aaaa:q:gs/a/t/</code>

<code>## 此時,最多的替換出現在!:0,兩次!</code>

但多次測試結果來看,第零個詞彙被替換最多兩次,其他隻替換一次。具體原因暫時未知!

<b>原文釋出時間為:2015-06-19</b>

<b>本文來自雲栖社群合作夥伴“linux中國”</b>

繼續閱讀