分類: Linux

Linux 指令歷史紀錄(History)的操作教學與範例

這裡教大家如何善用 Linux 指令歷史紀錄,讓你在使用終端機的命令列時更有效率。

如果你是一個 Linux 的老手,你應該會非常習慣在桌面上開啟終端機,靠著鍵盤來進行主要的工作,像我個人平常的工作就是這樣,UNIX 與 Linux 對我個人而言最棒的功能就是終端機的命令列,只要對於各種的指令夠熟悉,能夠做的事情就會超乎你的想像。

然而長時間在終端機中以指令來操作時,如果能善用命令列的各種功能,將對於工作效率非常有幫助,其中指令的歷史紀錄(history)就是一個很常見、也非常有用的功能,如果你常常使用終端機,這個功能很值得一學。

如果你還沒有很習慣在終端機中敲一堆指令做事,那也沒關係,指令的歷史紀錄也可以幫助你在使用命令列時不用老是敲一堆的指令,甚至你會感覺其實歷史紀錄的功能有時候也很炫。(當你對於 Linux 的各種指令慢慢熟悉之後,就會了解為什麼那麼多人都那麼喜歡直接使用終端機工作,當然這需要一些時間與毅力。)

以下是一些關於 history 指令的一些使用技巧與範例。

使用 Ctrl + r 搜尋指令歷史紀錄

這個搜尋指令歷史紀錄的功能非常好用,它有可能是你最常用的一個功能!

在使用命令列時,如果你之前已經執行過一個很長的指令,而現在要再執行一次相同或是類似的指令,大家最常作的動作就是用鍵盤上面的向上鍵,找尋之前執行過的歷史紀錄,但是如果這個指令是比較早以前執行過的,那就要一直按向上鍵慢慢找,有時候找的時間搞不好都比重新敲指令來的長(我以前就是這樣)。

其實還有另一種方式,可以讓你更節省時間,就是使用 Ctrl + r 來直接搜尋指令的歷史紀錄,這就好像你在一般的文字檔中用一些關鍵字來搜尋一樣,使用這個功能你就可以直接輸入一些關鍵字來找尋之前執行過的指令,假設我們想要找尋有 apt 字眼的指令,那麼你可以這樣做:
在指令列上直接按下鍵盤的 Ctrl + r,這時候命令提示字元(prompt)就會變成類似

(reverse-i-search)`':

這個樣子,這個時候就可以直接輸入要搜尋的關鍵字,也就是「apt」。而在你輸入的同時,你會看到它也會不斷的顯示搜尋結果,當你看到你要的指令找到的後,就可以不用再繼續輸入了,通常如果你的關鍵字選得好,只要輸入幾個字母就可以找到你要的指令了。

當你看到你要的指令之後,如果你只是想要執行相同的指令,可以直接按下 Enter 鍵執行,但是如果你想要稍微修改一下指令的內容,那麼你可以按下鍵盤的左鍵或右鍵,他就會跳回一般命令提示字元的模式,這樣你就可以自己任意修改了,修改好了之後,就跟一般的指令一樣直接執行即可。

另外如果你在找尋指令時,剛好關鍵字選的不好,有太多指令都有相同的關鍵字時,你可以在輸入完關鍵字之後,再按下 Ctrl + r 找下一個含有關鍵字的指令,這就好像在文字檔中搜尋時,不斷地按「下一個」的概念。

舉例來說,如果我們要找尋 vi xorg.conf 這個指令,而當你輸入「vi」這個關鍵字之後,發現有很多指令都含有「vi」這個字眼,這時候就可以重複按下 Ctrl + r,直到我們找到我們要的指令為止,而找到之後,按下 Enter 就可以直接執行。

這個功能一開始不是那麼直覺,但是等你習慣之後,就會發現它很好用,而且當你的朋友看到你會這招,也會感覺你很厲害。:)

使用 HISTTIMEFORMAT 顯示時間戳記

一般在命令列執行 history 時,在預設的情況下輸出會包含指令的編號與指令內容,就像這樣:

664  sudo su
665  history
666  ls
667  ls -al
668  history

而有時候如果想要仔細檢查過去執行過的指令,並對照系統的狀態時,可以透過設定 HISTTIMEFORMAT 這個環境變數來顯示時間戳記:

export HISTTIMEFORMAT='%F %T '
history

輸出就會變成

664  2013-10-07 21:37:34 sudo su
665  2013-10-07 21:37:37 history 
666  2013-10-07 21:41:49 ls
667  2013-10-07 21:41:51 ls -al
668  2013-10-07 21:41:53 history

這樣就可以很清楚看到哪個時間點執行過哪個指令。

重複執行上一個指令

如果要重複執行上一個指令,有四種方式可以使用:

  • 使用鍵盤的向上鍵,它可以觀看之前執行過的指令,而按下 Enter 鍵即可執行。
  • 輸入 !! 在按下 Enter 鍵,就會執行上一個指令。
  • 輸入 !-1 在按下 Enter 鍵,就會執行上一個指令。
  • 按下 Ctrl + P 會顯示上一個指令,而按下 Enter 鍵即可執行。

從指令歷史紀錄中選擇一個指令執行

有時候當你已經幾乎忘記之前執行過哪些指令時,你可能會直接使用 history 指令查閱所有的指令,就像這樣:

history | more

輸出類次這樣

1  service network restart
2  exit
3  id
4  cat /etc/redhat-release

然後找到要執行的指令再用滑鼠複製貼上,而如果你只是要重複執行某一個指令的話,可以直接使用 !n 的方式,這裡的 n 就是 history 輸出中的編號,以這個例子來說,如果你想要執行編號 4 的 cat /etc/redhat-release 這行指令,就可以使用

!4

輸出為

cat /etc/redhat-release
Fedora release 9 (Sulphur)

你可以注意到在輸出的第一行會把實際執行的指令顯示出來,第二行之後才是該指令執行後的輸出,這樣也可以方便確認這個是不是你所要執行的指令。

執行以某個關鍵字開頭的指令

如果想要從令的歷史紀錄中執行一個以某個關鍵字開頭的指令,可以使用 !keyword,其中 keyword 就是指定的關鍵字,這樣的方式會從歷史紀錄中找出最近的一個符合的指令來執行。

例如有時候我們會在命令列做一系列的工作,但定期會要檢查系統的行程(process)狀態,也就是使用類似 ps aux | grep yp 這樣的指令,這種狀況除了第一次需要以鍵盤輸入之外,後來重復執行時,就可以直接使用

!ps

這樣的方式,也就是執行上一個以 ps 開頭的指令,當然如果你在工作時,都不會使用到以 p 開頭的其它指令,那你也可以使用

!p

這指令個輸出也會先把實際的指令顯示出來:

ps aux | grep yp
root     16947  0.0  0.1  36516  1264 ?        Sl   13:10   0:00 ypbind
root     17503  0.0  0.0   4124   740 pts/0    S+   19:19   0:00 grep yp

更改指令歷史紀錄的大小

一般在預設的狀況下,歷史記錄會保留 500 筆(至少目前的 bash 是這樣),而這個值可以透過 HISTSIZEHISTFILESIZE 兩個環境變數來指定,HISTSIZE 是用來指定歷史紀錄要保留的筆數,而 HISTFILESIZE 則是指定要儲存在檔案中歷史記錄筆數。

例如若只想要儲存 450 行的歷史紀錄,可以直接在 ~/.bash_profile 中加入下面兩行:

export HISTSIZE=450
export HISTFILESIZE=450

然後登出在重新登入之後,就會生效了。

更改指令歷史紀錄檔檔案名稱

在預設的狀況下,指令歷史紀錄會儲存在 ~/.bash_history 這個檔案中,如果想要更改這個預設的儲存位置,可以使用 HISTFILE 這個環境變數來指定。例如若要將儲存位置改成 ~/.commandline_warrior,則可在 ~/.bash_profile 中加入一行

HISTFILE=~/.commandline_warrior

不過更換這個儲存位置可能不常用。

刪除連續且重複的歷史紀錄

有時候我們會連續重復執行同一個指令好幾次,而在這樣的狀況下,在歷史紀錄中也會造成連續且重復的紀錄,而這樣的記錄其實是沒有什麼用的,若要刪除這些連續且重複的歷史紀錄,可以將 HISTCONTROL 這個環境變數設定為 ignoredups

舉例來說,如果我們執行三次同樣的 pwd 指令,然後再以 history 查詢:

pwd
pwd
pwd
history | tail -4

輸出的時候就會有重復的問題

267  pwd
268  pwd
269  pwd
270  history | tail -4

而設定了 HISTCONTROL 這個環境變數之後,就會改善:

export HISTCONTROL=ignoredups
pwd
pwd
pwd
history | tail -3

輸出就會變成

271  export HISTCONTROL=ignoredups
272  pwd
273  history | tail -3

這樣多餘的 pwd 就會被刪掉。

刪除所有重複的歷史紀錄

上一個例子是只將歷史紀錄中連續且重複的指令刪除,而如果想要刪除整個歷史紀錄中所有重複的指令,可以把 HISTCONTROL 設定為 erasedups。例如:

export HISTCONTROL=erasedups
pwd
service httpd stop
history | tail -3

輸出為

38  pwd
39  service httpd stop
40  history | tail -3

而將 HISTCONTROL 設定為 erasedups 之後:

ls -ltr
service httpd stop
history | tail -6

輸出為

35  export HISTCONTROL=erasedups
36  pwd
37  history | tail -3
38  ls -ltr
39  service httpd stop
40  history | tail -6

這裡的第二個 pwd 就不見了。

讓某些指令不要紀錄在歷史紀錄中

在使用命令列時,如果想讓某些指令不要被紀錄在歷史紀錄中,可以將 HISTCONTROL 設定為 ignorespace,在這樣的情況下,所有以空白開頭的指令都不會被紀錄起來,例如:

export HISTCONTROL=ignorespace
ls -ltr
pwd
 service httpd stop
history | tail -3

輸出為

67  ls -ltr
68  pwd
69  history | tail -3

請注意這裡的第三行指令 service httpd stop 之前有一個空白字元,而這行指令因為是以空白字元開頭的,所以就不會被紀錄起來。

有些系統的 HISTCONTROL 預設值是 ignoreboth,這個值代表同時啓用 ignoredupsignorespace,這種狀況除了會忽略重複的指令之外,也會把以空白開頭的指令拿掉。

暫時清除所有的歷史紀錄

使用向上鍵或是 Ctrl + r 查詢指令的歷史紀錄是個很不錯的功能,但是如果你的歷史紀錄累積了很久,常常搜尋時就會找到以前沒用的指令,這種時候就可以將以前的歷史紀錄暫時清除:

history -c

這樣會把目前的所有歷史紀錄暫時全部清乾淨,而在重新登入之後,還是會回覆以前紀錄,不用擔心把不該刪的東西砍了。(至少我測試的結果是這樣)

上一行指令參數的替換

有時候我們在使用指令的歷史紀錄時,會需要執行不同的指令,但是其參數是一樣的,例如:

ls long-long-filename1.txt long-long-filename2.txt
cat long-long-filename2.txt

在這種狀況下,在執行第二行指令時,大部分的人都會直接使用向上鍵,叫出上一個指令,然後再把第一個 ls 改成 cat,再砍掉沒用的 long-long-filename1.txt,但是其實可以有更快的方式,就是使用歷史紀錄替換的功能:

ls long-long-filename1.txt long-long-filename2.txt
cat !$

這裡的 !$ 就是指上一行指令的最後一個參數,在這裡就是 long-long-filename2.txt,這樣執行起來跟原來的使用方式是一模一樣的,只不過在執行前他會先顯示實際執行的指令,之後才是該指令的輸出,這個例子第二行指令的輸出就會類似這樣:

cat long-long-filename2.txt
...

如果想要替換成上一行指令的第一個參數,則可使用 !^

ls long-long-filename1.txt long-long-filename2.txt
cat !^

輸出會像這樣

cat long-long-filename2.txt
...

若要替換成上一行指令所有的參數,則可使用 !*

ls long-long-filename1.txt long-long-filename2.txt
cat !*

這樣輸出就會變成:

cat long-long-filename1.txt long-long-filename2.txt
...

如果上一行的參數真的很多,而你只要其中的一個,不是第一個也不是最後一個,這種情況也可以用 !:n,直接指定要用哪一個,其中 n 是一個數字,指定要替換第幾個參數。例如若要替換成上一行指令的第二個參數:

ls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt
cat !:2

輸出為

cat long-long-filename2.txt
...

指定指令參數的替換

上面介紹的指令參數替換是針對上一行指令的參數,若要替換成更早之前的指令參數,那麼也可以直接使用指令開頭的關鍵字來指定,像若要替換一個以 key 字眼開頭指令的最後一個參數,就使用 !key:$,例如:

ls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt
pwd
cat !ls:$

輸出為

cat long-long-filename3.txt
...

而若要替換成其他位置的參數,用法都類似。第一個參數:

ls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt
pwd
cat !ls:^

輸出為

cat long-long-filename1.txt
...

所有參數:

ls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt
pwd
cat !ls:*

輸出為

cat long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt
...

第二個參數:

ls long-long-filename1.txt long-long-filename2.txt long-long-filename3.txt
pwd
cat !ls:2

輸出為

cat long-long-filename2.txt
...

停用指令歷史紀錄

如果你想要直接停用指令歷史紀錄的功能,可以將 HISTSIZE 設為 0,這樣所有的指令都不會被記錄下來:

export HISTSIZE=0
history

輸出就什麼都沒有。

讓歷史紀錄忽略某些常用指令

一般在命令列中,我想大家最常用的指令就是 ls 這類的簡單指令,而這樣的指令其實放在歷史紀錄中沒什麼用處,只是多佔空間而已,如果你想要讓歷史紀錄不要記錄這些沒用處的指令,可以透過設定 HISTIGNORE 這個環境變數來達成,你可以將不想記錄的指令列表放進這個變數中,不同指令以冒號分隔,這樣這些指令就會被自動忽略。例如:

export HISTIGNORE="pwd:ls:ls -ltr:"
pwd
ls
ls -ltr
service httpd stop
history | tail -3

輸出為

79  export HISTIGNORE="pwd:ls:ls -ltr:"
80  service httpd stop
81  history

這樣那些沒用的指令就不會佔去歷史紀錄的空間。這裡要注意一點,在 HISTIGNORE 指定要忽略的指令時,必須要很明確指定要忽略的指令,即便是只有參數不同,也會被視為不同的指令,像 lsls -ltr 這兩個就會被當成是不一樣的,所以在這個例子中,如果你執行 ls -l 的話,也還是會被記錄起來。

列出最近幾筆歷史紀錄

在看歷史紀錄時,因為歷史紀錄通常都很多,一般人會使用

history | more

這樣的方式來看,而若要看最近的幾筆記錄,可以使用 history n 來看最近 n 筆記錄,例如若要看最近 10 筆紀錄,則使用:

history 10

參考資料:The Geek StuffTecmintThe Geek Stuff

G. T. Wang

個人使用 Linux 經驗長達十餘年,樂於分享各種自由軟體技術與實作文章。

Recent Posts

光陽 KYMCO GP 125 機車接電發動、更換電瓶記錄

本篇記錄我的光陽 KYMCO ...

2 年 ago

[開箱] YubiKey 5C NFC 實體金鑰

本篇是 YubiKey 5C ...

3 年 ago

[DIY] 自製竹火把

本篇記錄我拿竹子加上過期的苦茶...

3 年 ago