=> 上一篇:你會寫 Emacs 命令嗎?
前言
也許你早已注意到,當你用 Emacs 編輯文件時,例如編輯 foo.txt 文件,在完成編輯後,你會使用已頗為熟悉的組合鍵 C-x C-s 將 Emacs 的 foo.txt 緩衝區裏的內容保存至 foo.txt 文件。此時,你會發現,在 foo.txt 文件所在的目錄裏,會出現名為 foo.txt~ 的文件。
也許你還注意到了另一個事實。當你在編輯 foo.txt 文件的過程中,若有所停頓,偶爾你會看到微緩衝區會閃過「Auto-saving...done」字樣,同時在 foo.txt 文件所在目錄下,會出現名為 #foo.txt# 的文件,其內容與 foo.txt 緩衝區內容相同。當你對 foo.txt 緩衝區執行 C-x C-s 後,#foo.txt# 文件便自動消失。
這兩件事實際上是 Emacs 的文件保護機制的外在表現。很多 Emacs 教程對文件保護機制似乎不以為然,有一些嫌棄像 foo.txt~ 這樣的文件有礙觀瞻,便在 init.el 文件裏作以下設定:
;; 禁用自動備份功能
(setq make-backup-files nil)
你可以如此設定,這是 Emacs 賦予你的自由,不過我還是建議你知悉詳情後再作定奪,而不是從一些 Emacs 高手的 init.el 中抄來這一設定,並以為自動備份機制是 Emacs 無用甚至不好的功能。
備份
像 foo.txt~ 這樣的文件,是 Emacs 為 foo.txt 文件自動生成的備份。你可以基於以下試驗,理解其存在的意義和用法。
在某個目錄下,用 Emacs 新建一份文件 foo.txt,亦即在 Emacs 裏開啓一個名為 foo.txt 的緩衝區,其內容如下:
this is foo.txt.
當你用 C-x C-s 將上述內容保存至文件 foo.txt 後,此時 Emacs 並不創建 foo.txt~ 文件。
現在,用 Emacs 再次打開 foo.txt 文件,將其內容修改為
this is foo.txt.
there are some modifications.
然後,再次保存,此時 Emacs 便會創建 foo.txt~ 文件,其內容為
this is foo.txt.
亦即 foo.txt~ 文件存儲的內容是修改前的 foo.txt 文件內容,故而可以認為 foo.txt~ 文件是 foo.txt 文件的上一個版本。
倘若你對 foo.txt 所作的修改並不滿意,希望能回退到它的上一個版本,在 Unix 或 Linux 系統裏,只需在 Shell 裏執行以下命令:
$ mv foo.txt~ foo.txt
然後用 Emacs 重新打開 foo.txt 文件,再作編輯即可。
練習:倘若你剛才修改了 foo.txt 文件並予以保存,但此時 Emacs 並未退出,即依然存在 foo.txt 緩衝區,請執行 C-x x g,觀察所發生的現象。
補丁
倘若你懂得如何使用 diff/patch 工具,那麼 Emacs 為文件保存的上個版本,也能幫助你製作文件補丁。例如
$ diff -u foo.txt~ foo.txt > foo.patch
diff 命令可以分析 foo.txt~ 和 foo.txt 的差異,並將分析結果寫入 foo.patch 文件。你可以將 foo.patch 發送給他人。若後者的機器上同樣存在 foo.txt 文件,且其內容與你機器上的 foo.txt~ 文件相同,他可使用 patch 命令,引入你對 foo.txt 所作的全部修改。
$ patch foo.txt < foo.patch
對於打了補丁的 foo.txt,只要補丁文件尚在,也可以使用 patch 命令撤銷補丁,例如
$ patch -R foo.txt < foo.patch
diff 和 patch 都是源自 Unix 系統的偉大的小工具,有必要用一些時間熟悉它們的用法,並在與他人在合作編寫同一份文檔時使用它們,可以有效實現工作同步。
關聯列表
Emacs 會自動為你編輯的文件備份上一個版本,即使你從未使用過這個備份,但只要它存在,你便會有一些肆意妄為的底氣。讓你覺得難以接受的,並非是這種機制,而是你不希望文件目錄裏有一堆備份文件。
實際上,Emacs 允許你設定一個目錄,統一存放所有備份文件。例如,你可以在 $HOME/.emacs.d 目錄創建 backups 目錄:
$ cd ~/.emacs.d
$ mkdir backups
在 Linux 的 Shell 裏,$HOME 通常可以簡寫為 ~。
然後在 init.el 裏添加以下配置:
;; 備份文件存放目錄
(setq backup-directory-alist '(("." . "~/.emacs.d/backups")))
上述表達式中的 setq,你已經很熟悉它了。你應該還不懂 Elisp 的關聯列表。backup-directory-alist 便是一個關聯列表,其值是隻包含一個元素的列表:
'(("." . "~/.emacs.d/backups"))
你可以通過以下代碼,明白何為關聯列表。
(let ((this-list '(("foo" . "i am foo!")
("bar" . "i am bar!")
("hello" . "Hello world!"))))
(dolist (it this-list)
(message "%s: %s" (car it) (cdr it))))
在上述代碼中,this-list 是局部變量,其值是三個鍵值對構成的列表,這樣的列表即為關聯列表。鍵值對的形式為
(鍵 . 值)
之前,在配置字體時,我們曾用 dolist 遍歷過 Unicode 腳本列表。雖然 this-list 是鍵值對元素構成的列表,但終歸是列表,故而可用 dolist 遍歷。遍歷過程中,it 指向 this-list 的每個元素,用 car 可以獲得 it 所指代元素的「鍵」的部分,而 cdr 可獲得該元素的「值」的部分。
在 Emacs 裏對上述代碼求值,求值過程中,會輸出多行信息,而微緩衝區只能看到最後一行。在 Emacs 圖形界面裏,倘若你用鼠標左鍵點一下微緩衝區,Emacs 便會自動開啓新窗口,顯示 *Messages* 緩衝區中的所有內容,在該緩衝區裏,可以看到上述表達式求值過程中的全部輸出,如下:
foo: i am foo!
bar: i am bar!
hello: Hello world!
nil
前三行信息是 message 輸出。最後的 nil 是 Emacs 對 let 表達式的求值結果。將光標定位到 *Message* 緩衝區,摁 q 鍵便可關閉該緩衝區所在的窗口。
當你明白何為關聯列表後,對上述 init.el 中的文件備份目錄的設置代碼所剩的疑惑應該是鍵值對
("." . "~/.emacs.d/backups")
表示什麼?鍵 "." 表示所有的備份文件,值 "~/.emacs.d/backups" 表示存放所有備份文件的目錄。
當上述 init.el 的設定生效後,Emacs 便會將所有文件的備份存放於 ~/.emacs.d/backups 目錄。例如,倘若我在 ~/test/emacs 目錄內編輯 foo.txt 文件,則該文件在 ~/.emacs.d/backups 目錄裏對應的備份文件是
!home!garfileo!test!emacs!foo.txt~
倘若你將 ! 理解為 /,那麼上述文件名便可理解為,這份文件是 /home/garfileo/test/emacs 目錄中的 foo.txt 文件的備份。
自動保存
默認情況下,Emacs 會在每輸入 300 個字符(可通過 auto-save-interval 變量修改此值)或在 30 秒內無操作(可通過 auto-save-timeout 變量修改此值),便會自動將緩衝區內容保存。本文前言所述的名為 #foo.txt# 的文件,便是 Emacs 對 foo.txt 緩衝區內容自動保存的結果。
也許你會覺得奇怪,為何 Emacs 不直接將緩衝區內容自動保存到對應的文件裏呢?例如,將 foo.txt 緩衝區的內容自動保存到 foo.txt 文件裏。實際上,Emacs 可以如你所願,只是需要你在 init.el 裏予以配置:
;; 直接保存到正在編輯的文件(非臨時文件)
(auto-save-visited-mode 1)
但是我不建議你如此設定,原因是,若 Emacs 每次按這一設定,將緩衝區內容保存到所編輯的文件,則意味着要對文件的上一個版本作一次備份,而整個過程是在你毫無察覺的情況完成的。有時,你需要的某個備份,會被 Emacs 的自動保存機制悄無聲息的篡改了,而 Emacs 默認的自動保存機制,可以完美兼容文件的自動備份機制。
倘若你決定繼續使用 Emacs 默認的基於臨時文件的自動保存機制,你需要掌握如何基於該臨時文件對抗一些意外風險。例如,當你正在編輯一份文件時,可能會有一些意外發生,導致計算機突然關機,你對文件的許多改動會因未能及時保存而丟失。當你重新開機後,便可基於 Emacs 自動保存的臨時文件,恢復這些改動。
以下試驗,可以模擬上述的意外場景。用 Emacs 編輯前文所述的 foo.txt,令其內容變為
this is foo.txt.
there are some modifications.
more modifications!
然後略等約 1 分鐘,知道你看到在 foo.txt 文件所在目錄出現 #foo.txt# 文件,然後記住,不要保存 foo.txt 緩衝區,而是直接關閉 Emacs。這時,Emacs 會提醒你,是否保存對文件的修改,你選擇「Close Without Saving」,如此便模擬了類似計算機突然關閉,而你來不及保存文件內容的場景。
之後,你在 #foo.txt# 文件所在的目錄裏,重新打開或創建 foo.txt 文件時,Emacs 會在微緩衝區裏提示:
foo.txt has auto save data; consider M-x recover-this-file
當你執行 M-x recover-this-file 後,Emacs 會在微緩衝區詢問:
Recover auto save file /home/garfileo/test/emacs/#foo.txt#? (yes or no)
此時,在微緩衝區輸入 yes RET,便可在 foo.txt 緩衝區裏恢復在意外關機前 Emacs 為你自動保存的內容,然後再正常將其保存至 foo.txt 文件,之後 #foo.txt# 便事了拂衣去,深藏功與名,除非下一次意外關機,你才能再一次看到它。
總結
我雖身為 Emacs 多年的資深新手,但是和你一樣,也是初次使用 Emacs 的文件自動備份與保存機制。我們也學習了 Elisp 的關聯列表,它像一個詞典,只是缺乏索引,無論從中查找什麼,都需要對整個列表遍歷一次。
=> 下一篇:緩衝區——Emacs 無形之象