天天看點

Bash之指令曆史的存儲和記錄

Bash之指令曆史的存儲和記錄

在bash中我們可以使用 history 指令回顧,修改和重用之前使用過的曆史指令。去掉信号機制、去掉作業控制、去掉各種參數調用,今天我們隻看下bash中如何記錄指令,如何存儲曆史指令。

在 bash 的源代碼中,history 指令的定義代碼為 builtins/history.def , builtins 目錄下存放的是内部指令的源代碼,每個内部指令是一個def檔案,如history.def,cd.def等。 makefile中defsrc聲明了所有内部指令的def檔案。由 mkbuiltins.c 生成編譯時輔助工具 mkbuiltins,mkbuiltins

處理 *.def 檔案,生成指令的 *.c 源程式以及 builtins.c 、 builtext.h。 builtins.c 和 builtext.h 相當于各個内部指令的索引。

history.def 的作用是解析指令并将不同的參數分發指令到不同的功能實作,如讀取指令、覆寫指令等。一些功能實作代碼檔案在

history指令在顯示時會顯示所有的曆史記錄,這裡的所有曆史記錄包括最開始從檔案中讀取的曆史記錄,還包括目前會話産生的記錄。假設你的曆史記錄中已經有了500條指令,如果你用其它文檔編輯器将曆史日志檔案寫到1000條,打開終端,你會發現顯示的還隻有500記錄。這是因為在打開終端初始化時,不僅僅有曆史記錄清單的讀操作,還會有關于檔案記錄數限制的初始化操作,確定檔案中的記錄條數不會大于設定的最大值。這個初始化操作在 load_history

函數中實作,函數最開始就做了兩次曆史記錄檔案的截取操作,一次是預設500,一次是設定的最大值: histfilesize 。

在曆史記錄中有一個會話的概念,不同會話中的指令在沒有儲存到檔案前是不會互相沖突的。比如,打開終端a如果你删除 .bash_history 的前10行指令,儲存,在指令行中輸入history,你會發現輸出的指令曆史記錄并不是從1開始,而是從11開始的。如果此時,你再開一個終端b,輸入若幹條指令後,再輸入history,你會發現曆史記錄中沒有在終端a輸入的指令。這是由于在一個終端會話中,曆史記錄從固化存儲位置的讀取操作隻有一次,寫入操作也隻有一次,即在打開終端時讀取,在關閉終端時寫入。

除了檔案存儲,曆史記錄也可以記錄在mmap,對應的宏定義為 history_use_mmap。

如果以檔案存儲方式,指令記錄以一行一條指令的方式存儲在.bash_history(預設,如果有設定 histfile 則優先使用 histfile 中的值),雖然我們使用 history 指令時看到每條指令前都會有一個标号,我們可以使用 !标号 的方式重新執行指令,這個标号并不是唯一不變的,标号是在初始化時在讀取檔案時在程式操作中标記的,後續有指令加入時,标号會自增,這個自增并不會受曆史記錄的最大值限制。

當一次會話退出時儲存曆史記錄檔案,将曆史資料固化存儲,并将會話統計清零。當存儲到檔案時,bash 會将此前會話中的指令直接存儲到檔案末尾,如果檔案的記錄數大于定義的最大記錄數,則清空舊的曆史指令,并且當下次再存儲時會重寫此前檔案。代碼示例如下:

Bash之指令曆史的存儲和記錄

存儲操作最終還是歸集于存儲媒體的讀寫操作,如對檔案的讀寫,增加的隻是對業務邏輯規則的各種限制。指令可以在執行指令時記錄,也可以在指令剛輸入,但已經識别的情況下記錄,bash 屬于後者。 bash 在 yacc 做文法分析時将使用者輸入的指令通過

maybe_add_history 函數寫入到目前會話的指令曆史記錄表中。在做文法分析時就已經記錄了使用者輸入的指令,此時記錄就不用管指令最終的結果是怎樣,也不用管如果執行過程出了異常會怎樣處理。它隻是如實的在執行前記錄使用者輸入的什麼指令,由此,我們可以定義 bash 的指令曆史記錄的定義為使用者輸入的指令曆史記錄。

參考資料: http://files.linjian.org/articles/techreport/bash_study.tar.gz