網路農夫 --  Home 網路農夫 --  Home
網路農夫 --  Home
wait.....

3-5 history 的設定與運用

history 說起來可算是 C Shell 的重大功能之一。history 這個字如果以它的整體功能來說,或許我們可稱它為 UNIX 的指令使用記錄。它負責記錄使用過的指令,供使用者“再利用”。這個“再利用”的動作可說是靈活多變,可運用的方面除了指令行外,連別名(aliases)、預設變數(Predefined Variables)的設定或 shell script 的程式設計等均可見到 history 的運用蹤跡。實在是一項使用者不能忽略的好功能。

 

3-5-1 製定 history 的使用環境

 

要能善用 history,首先得熟悉關於 history 使用環境的三個重要的預設變數。以下讓我們來為你一一地介紹:

$history 變數

 

變數 history 是使用 history 功能之前必須先設定的。通常都在 C Shell 的啟始檔案“.cshrc”中設定此變數,當然也可用手動的方式來設定或更改。設定的語法如下:

set history = n (n是數字且必須是整數)

如果你把 n 設定為 30,則 C Shell 將會隨時為你保留最後所下的 30 道指令,供你呼叫顯示到螢幕上,這便是 history list,它可供你運用 history 的其它功能。如果你想查看此變數的設定值,可使用指令

echo $history

來顯示 history 設定值。要重新設定可修改“.cshrc”檔案內的設定,再以指令 source .cshrc 便能更新設定值。也可直接下指令 set history = n 來更改。但後者設定的設定值會隨著該 Shell 的終結而消失,此點請注意。

$savehist 變數

 

設定這個變數的作用是在 Shell 終結後,將最後某幾道指令儲存到使用者的 home 目錄下的“.history”特殊檔案中,以供你下次 login 或另一個還在工作的 shell 來運用,上一個 shell 所記錄下來的最後幾道指令。如你設定為下:

set savehist = 50

則每一個 shell 的終結,都會對“~/.history”檔案做資料寫入、更新的動作。請注意!再重覆一遍,是每一個 shell 的終結都會更新 home 目錄下的“.history”檔案。所以當你下次 login 時所見到的 history list,便是“.history”檔案的內容。

 

如果你常在 X Windows 或者是 Open Windows 等 GUI Windows 界面下工作的話,相信你一定會開好幾個 Windows 同時工作。在這種工作環境下如果你不去加以控制要儲存那一個 shell 的話,建議你不要使用它,或許會比較好些。

$histchars 變數

 

這個變數便是用來改變設定 history 的運用符號。用來解決符號的使用習慣問題。history 的專屬符號有兩個,第一個使用的符號“!”第二個符號“^”。如果你如下設定:

set histchars = "#/"

則符號“#”取代符號“!”;而符號“/”取代符號“^”。老實說來 UNIX系統對特殊符號的運用可說到了一“符”多“棲”的田地了,小心改出毛病來。建議你謹慎使用(最好是不用)。

3-5-2 history 的運用說明

3-5-2-1 設定 history 的數量與顯示的關係

在一般使用的情況下,history 的設定數量大約都在 20 ~ 50 之間。這是因為設定太大了會佔用系統資源,用不上的話實在不划算。適中夠用就是好的設定值。你也可就螢幕顯示的列數來做為設定值的參考。免得列出history 時超過螢幕所能容納的行數。要在螢幕上列出 history 相當簡單,鍵入 history 便可。如果你嫌“history”字太長不好記,設個別名“alias h history”,相信也不會有人罵你懶!

 

在指令行模式下鍵入 history 可顯示出 history list。當然你也可以在指令 history 後加上一個數字,用來控制顯示 history list 的數量。如下:

25 % history 5
21 ls
22 cd subdir1
23 ls
24 cl
25 history 5

在 BSD 版本的 UNIX 系統中,C Shell 會提供 history 一項比較特殊的選項,就是“-r”選項。它的作用是將 hisroty list 顯示的事件次序倒過來。如下所示:

26 % history -r 5
26 history -r 5
25 history 5
24 cl
23 ls
22 cd subdir1
27 %

較長的設定值在顯示上會有超出螢幕行數的情況產生,這個問題很好解決,提供幾種別名設定供你參考使用:

alias hup 'history | head -15' (前15道history)
alias hdn 'history | tail -15' (後15道history)
alias hm 'history | more' (一頁一頁看)

3-5-2-2 history 對過去指令的處理方式說明

 

C Shell 的 history 功能,對於使用者所執行過的每個指令,基本上都把它當成一個“事件(event)”來處理。而每個事件都以數字來編號代表之。讓我們來在設定預設變數 $prompt 中加入 history 功能來說明,這種事件的處理情況。在指令行中鍵入如下:

% set prompt = '\! % '

這樣你便可在提詞(prompt)中清楚地看到 history 對“事件”的編號情況。如以下實際例子:

% set prompt = '\! % '
13 % history
4 cd test
5 \rm -r test
6 tar cvf /dev/rst8 backup &
7 ls
8 vi backup.task
9 set history = 50
10 ps axu | grep cron
11 cd
12 set prompt = '! % '
13 history
14 % echo $history
10

從以上實例中看到 history 對過去指令的編號情況。通常 history 對指令的編號由 1 開始編起,每次自動加 1。但如果你設定預設變數 $savehist,則由此變數的數字加 1 算起。此點請注意。

 

到此相信你對 history 的運作應該有一個概略的瞭解了吧!接下來便開始來運用 history 的功能,來“便利”我們的指令操作。如果你能善用將可大大改變你的使用效率。它至少可以降低你因為打多了鍵盤而得到職業病的機率。

3-5-2-3 history 的符號說明與基本運用

 

我們已經知道 history 對指令是以事件來處理,且加以編號。要如何簡單地“再利用”這些過去的“事件”呢?history 定義了一群符號來供我們變化使用。這些符號都是組合式的,基礎的第一個符號是“!”。此符號用來啟動 history 功能,但緊接在符號後的不能是下列這些符號:TAB 鍵、空白鍵、換行、等號或“(”及“.”等。接上這些符號將會產生錯誤。

history 在指令行運作下的常用到的符號運用組合,我們以功能分類來加以說明:

 

1. 指定事件執行

符號“!!”

“!!”執行上一個指令。在運用上除了執行上個指令外,並可在符號後在加入與指令語法不相沖的字元來修飾上個指令。如下例:

 

% ls
akira.sch passwd

 

以上是指令 ls 的執行情況,當你覺得所顯示的訊息不足,想要加上選項-l時,你可在運用 history 時再加上該選項,如下用“!! -l”便相當於下指令“ls -l”:

 

% !! -l
total 2
1 -rw-r--r-- 1 akira 184 Sep 16 11:32 akira.sch
1 -rw-r--r-- 1 akira 65 Sep 16 11:31 passwd

 

符號“!n”

“!n”執行第 n 個事件。注意到這第 n 個事件一定得還在 history list 內才能順利執行。

假設我們的 history 變數設定值為 10,當我們執行到第 22 個事件時,想要用符號“!n”來執行第 5 個事件,會產生什麼情況呢?請看下面:

 

21 % echo $history
10
22 % !5
5: Event not found.
22 %

 

“5: Event not found.”這個錯誤訊息便明顯的告訴了我們,指令所要執行的 history 事件以不在 history list 的範圍之內。如果你常會有這種情況發生,可以考慮將 history 變數的設定值加大到適當的數字。另外請注意到在事件 22 執行指令“!5”時,因產生“5: Event not found.”的錯誤,history 對此錯誤的指令並不記錄下來,所以事件的編號停留“22”。如果你所指定的事件在 history list 的範圍內的話,指令便能順利的執行過去的事件。如下:

 

22 % !17
ls
total 4
2 cshrc* 1 file 1 passwd

 

符號“!-n”

“!-n” 執行在 history list 中倒數第 n 個事件。

我們用下面的例子說明它的使用情況:

 

24 % h
15 ps axu | grep cron
16 pwd
17 ls
18 vi passwd
19 h
20 cl
21 echo $history
22 vi ~/.cshrc
23 ls
24 h
25 % !-4
echo $history
10
26 %

 

看到沒有事件 25 的指令“!-4”所執行的是 history list 的事件 21,剛好是倒數第四個。

2. 搜尋指定執行

“!string” 搜尋以某字串 string 為開頭的過去指令並加以執行。

 

“!?string?” 搜尋過去指令行中有某字串 ?string? 並加以執行。

 

以上兩種使用方式均是在 history list 的 event 中,搜尋 event 的開頭或 event 中間有指定的字串,來加以執行。但均是執行第一個符合條件的 event。搜尋時以由 event 數字大到小。如下例所示:

 

34 % h
25 h
26 cd
27 tar cvf /dev/rst8 akbin &
28 ps
29 vi ~/.cshrc
30 tar tvf /dev/rst8
31 cd test
32 ls
33 vi passwd
34 h
35 % !v

 

在上例中最後指令“!v”所執行的是第33個“vi passwd”,而不會是第27個“vi ~/.cshrc”。如果要執行第 27 個 event,你可用“!?cshrc”來執行,當然你也可直接用“!27”來執行,但會使用搜尋方式,自然是在不記的第幾個 event 的情況下才會採用的,不是嗎?又如果你設定的 $history 為 100 個,要用眼睛在 history list 中找尋,倒不如“奴役”電腦還來得好。是嗎?

 

接下來介紹一個比較特殊的符號“!{...}”,用來應付一種比較特殊的情況。譬如你想在重執行以下這個指令:

 

% cat /etc/hosts > ~/hosts

 

並且要緊接著在指令後加入一些字串。如將重導向的“~/hosts”這個檔案名稱改為“~/hosts.bak”, 如果你下指令如下:

 

% !cat.bak
cat.bak: Event not found.

 

這種錯誤是因為搜尋的字串與添加的字串沒有區隔開來的結果。解決的方法是使用大括號“{ }”將搜尋的字串括起來,再僅接著添加的字串便可。如下所示:

 

% !{cat}.bak
cat /etc/passwd > ~/hosts.bak

3. 修改執行過去指令

下錯指令、打錯字的情況是難免會發生的,或大同小異的指令也偶而會連續使用,像此類的情況運用修改的方式相當適當。(由其指令又臭又長的時候,你使用時一定會感激造令者的偉大)先來看一個例子:

% rsh hosta -l user "screendump -x 20 -y 20 -X 300 -Y 300 scrfileA"

如果是像這種指令打錯了一個字,比方說指令 screendump 少打了一個字母 e,變成 screndump,天呀!主管在旁邊看耶!!重打一次?如果再打錯保證你年終獎金少一半!!!這可怎麼辦??不用急,露一手什麼叫“知錯能改”,如下:

語法 ^old^new^ 修改上個指令的old為new後執行。

實際指令 % ^re^ree^

就這樣把你漏打的字母補了進去。簡單幾個字就搞定,方便吧!不過這種方式的修改是有限制的。它只能還是以上面那個長的不得了的指令中更改一個地方,而且還得是上一個“事件”才行。如果不是上一道指令,要做類似的修改運用,可用以下的語法均可做到:

!n:s/old/new/ 修改第 n 個事件的 old 為 new 後執行。

!string:s/old/new/ 搜尋以 string 為開頭的事件並修改 old 為 new 後執行之。

到目前所介紹的三種方式,均有個共通性,就是只能更改一組字串。而且字串內不可有空白鍵。如果有空白鍵會造成修改錯誤。如果要同時修改兩個以上相同的字串,對後面兩種語法中再加一個“g”便可,如下:

!n:gs/old/new/ 修改第 n 個事件中所有的 old 為 new 後執行。

!string:gs/old/new/ 搜尋以 string 為開頭的事件並修改所有的 old 為 new 後執行之。

用這種方法便可輕易地將那個臭長指令中的 20,一起更改為想要的數字了。如下示:

% !rsh:gs/20/50/

這時我們所得到的將相當於下:

% rsh hosta -l user "screendump -x 50 -y 50 -X 300 -Y 300 scrfileA"

自己找例子試一試吧!它們真的是太好用了。

3-5-2-4 history 對於事件的引數(argument)運用

1. 引數的區分方式

在前面我們曾經提到 history 對指令是以編號的事件來處理,但你除了可再利用這些事件外,同時你也可再利用事件中的每一個引數。先讓我們來看一個底下這個例子:

事 件的引數由 0 開始編起,所以 0 便代表事件開頭的指令。接下來是以空白鍵來隔開的字串或以特殊符號依序編號。注意到引數的編號,並非全以空白鍵隔開才算,因為只要有特殊符號,不管它與前 後有無空白鍵都得算是一個單獨的引數。(這些特殊符號有 |,<,<<,>,>>,& 等)history 對引數安排了多種方式來代表引數的位置,如數字(0,1,2,...)或符號(“^”“$”“*”“x*”“x-”)。對引數的運用可單一的或者是一個範 圍(x-y)均可。

以下我們以表列來說明引數的符號使用方式:

符號

說            明

!!:n 使用上個事件的第 n 個引數,如“!!:3”
!!:x-y 使用上個事件的第 x 個引數到第 y 個引數,如“!!:0-4”
!n:^ 使用第 n 個事件的第 1 個引數,符號“^”代表第一個引數
!^ 使用上個事件的第 1 個引數
!$ 使用上個事件的最後一個引數,符號“$”代表第最後個引數
!* 使用上個事件的第 1 個引數到最後一個引數
!!:x* 使用上個事件的第 x 個引數到最後一個引數,(相當於“!!:x-$”)如“!!:2*”即代表使用第 2 個之後的所有引數
!!:x- 與上相似不同處為不包含最後的引數,如“!!:2-”即代表使用第 2個到最後的倒數第一個引數。(語法“!!:x-”就是“!!:x-$”去掉“$”後所剩下來的樣子)

2. 引數在指令行的運用

在指令行模式下有些時候會連續好幾個指令都用到同一個或一群檔名,也就事說,你可能會有一個字串要一再重複鍵入。比方有以下情況:

 

1 % ls -l ch1.doc ch2.doc ch3.doc
2 % chmod g+w ch1.doc ch2.doc ch3.doc
3 % tar cvf /dev/rst8 ch1.doc ch2.doc ch3.doc

 

像以上這接連的三個指令都用到相同的檔案名稱 ch1.doc,ch2.doc, ch3.doc,利用 C Shell 的 history 功能,我們可在指令中將過去事件的引數利用符號代入,以簡化指令的長度,如以下所示:

 

4 % ls -l ch1.doc ch2.doc ch3.doc
5 % chmod g+w !!:2*
6 % tar cvf /dev/rst8 !!:2*

 

指令 5 中的“!!:2*”就代表指令 4 中的第 2 到最後的引數,所以指令 5 相當於指令 2。同理指令 6 相當於指令 3。

再舉一個例子:

 

7 % ls -l *.doc *.txt
8 % rm -r !$
9 % cat *.doc > doc.files
10 % !6:0-2 !!$

 

指令 8 中的“!$”既代表指令 7 中的最後的引數,就是“*.txt”。指令 10 的使用在語法上是成立的。連用 history 功能來組成指令,第一組“!6:0-2”是使用事件 6 的第 0 到第 2 個引數,接著空白鍵,接著第二組“!!$”使用前一個事件的最後一個引數。所以此指令相當於下:

 

10 % tar cvf /dev/rst8 doc.file

 

這便是 history 的引數的組合運用。對了!相信你還記得下面這個又臭又長的指令吧!

 

% rsh hosta -l user "screendump -x 50 -y 50 -X 300 -Y 300 scrfileA"

 

如果現在要你運用 history 的修改功能來更改第二個“50”變為“60”的話,你該如何下指令?回憶一下過去所介紹過的“修改功能”中,只能更改第一個或全部都改,這只改第二個可怎麼改才好呢?相信上面的指令 10會代給你靈感的。想到了沒,答案如下:

 

% !rsh:0-7 60 !rsh:9*

 

我們用該事件的引數 0 到 7,而引數 8 便是要修改的對像,我們不引用。在“!rsh:0-7”之後,我們便加上“□60□”,再接上該事件的引數 9 到最後的引數。如此便能成功地將某個事件中的某個引數,代換成所需(“□”代表空白鍵)。運用這樣較麻煩的方法可補原先與修改功能的不足與缺憾。希望有一天這種方法能幫你解決一些困擾。

在 history 功能中有一項是僅將指令或所須的功能列出,而不執行。使用的符號為“:p”。這種列出不執行的方式,對比較複雜或比較沒有把握的組合運用是有幫助的。我們來修改第 10 個的使用語法成為只顯示而不執行,來看看它的用法:

 

10 % !6:0-2:p !!:$:p
tar cvf /dev/rst8 doc.file
11 %

 

如上指令 10 所得的結果顯示在下行,但並不執行。如果錯誤則可加以更正,如果正確無誤想要執行則再鍵入“!!”便可。

3. 引數的特殊符號運用

在 history 的引數運用上有一項是比較特殊的,它是關於路徑(path)的運用。一般也並不常運用在指令行模式之下,倒是在 shell 的程式設計上相當有用。不過我們在介紹時,還是將它運用在指令行模式之下,讀者可借此瞭解它的功能。

 

27 % !cat:p
cat /usr/adm/messages > message.1
28 % !!:1:t:p
messages
29 % !cat:1:h:p
/usr/adm

 

由上例中我們可藉由 history 的功能來將一個路徑分離成兩個部份,如 “/usr/adm/messages”可分離成,“/usr/adm”與“messages”。如果以前面這個情況來說,一個絕對路徑(full path)可將它分離成檔案所在的路徑及檔案名稱這兩項資料吧!這個運用情況,我們將在 shell 程式設計中再為你舉例說明。

3-5-3 關於 history list 的說明

 

history list 實際上便是你啟動 History 功能之後所產生的一個指令暫存器。這個指令暫存器會記錄的你下過的指令,也可稱之為事件(even)。儲存事件的數量便是你使用內建指令「set history = xx」所指定的數量。 history list 的內容允許被呼叫出來加以再次使用或者是修改使用,被呼叫出來的事件可以是整個事件也可以是事件的部份引數,在使用上有相當多的方式及符號,當然組合的變 化也相當多。同時這個 history list 的內容也可以在你 logout 前儲存成特殊檔案供你下一次 login 時再利用。

 

3-5-4 如何傳遞 hisrory list 到另一個 C shell 中

 

在前面我們曾提到變數 savehist,它的功能是可以在你結束 C shell 之後,把該 shell 的  history list 儲存起來供你下一次 login 時,或者是另一個 C shell 來使用,但如果你並不想終結現在正在操作的 C shell,卻需要將 history list 儲存起來,提供另一個 C shell 運用時。此時變數 savehist 的功能就不能適用了。我們得尋求變通的方式來達到這個目的。首先,將所需的 history list 數量重導向到一個檔案中儲存,如下所示:

33 % history -h 15 > history.15
34 % vi history.15

接著便是使用編輯器清除檔案內不必要的指令。然後退出 vi。這時另一個 C shell 便可以看見這個檔案“history.15”。這時我們便可使用內建指令 source -h 來將該讀取“history.15”檔案內的各行指令,將它們加入到這個 C shell 的 history list 中,如此便可以供我們來運用了。請見實際的指令操作:

10 % source -h history.15
22 %

(請注意,加上選項 -h 的作用在於只讀入指令,但並不執行。關於詳細的情況請參考內建指令 source 說明)

 

版本:Beta-2001-05 -- 網路農夫