バックアップファイルについての設定

(setq make-backup-files nil) バックアップファイル(*~)を作らない

make-backup-files

Emacsのデフォルト設定では,abc.txtという名前のファイルを更新すると編集前の内容が abc.txt~というファイルにバックアップされるようになっています. この設定は一部の古いUNIXのユーザにはおなじみの設定なんだけども, 普段はあまり有用でない…というかむしろ目障りなバックアップファイルがあちこちにつくられるため, おおむね不評な印象です. この設定をオフにしてバックアップファイルをつくらせないようにするには, make-backup-filesnilにしておきます.

バックアップファイルを~/.bakに集める

Emacsのデフォルト設定が不評なのは,あっちこっちに“ナントカ~”みたいなファイルが散らばるからで, バックアップがとられること自体は好ましいことなんだと思います. そこで,次のようにするとバックアップファイルが“~/.bak/”ディレクトリに集められるようになり, バックアップファイルが散らばる問題を回避しながらバックアップをとれるようになります.

make-backup-file.el
(setq make-backup-files t)
(setq backup-directory "~/.bak")
(if (and (boundp 'backup-directory)
         (not (fboundp 'make-backup-file-name-original)))
    (progn
      (fset 'make-backup-file-name-original
            (symbol-function 'make-backup-file-name))
      (defun make-backup-file-name (filename)
        (if (and (file-exists-p (expand-file-name backup-directory))
                 (file-directory-p (expand-file-name backup-directory)))
            (concat (expand-file-name backup-directory) 
                    "/" (file-name-nondirectory filename))
          (make-backup-file-name-original filename)))))

上のプログラムは,いろいろ考慮した結果,3行目のif以降が ずいぶん複雑になってるけど, もっと簡単に書くこともできます(そうしたほうが設定ファイルが簡潔になります). 以下にシンプルな方から順に説明します.

Emacsは バックアップファイルのファイル名として (make-backup-file-name "abc.txt")のように評価して得られた文字列を使います. なので,make-backup-file-nameを再定義してしまえば任意のファイル名でバックアップをとれます.

(defun make-backup-file-name (filename)
  (concat "~/.bak/" (file-name-nondirectory filename)))

とりあえず これでバックアップファイルを~/.bak/に集めることができるようになりました. ここで,file-name-nondirectoryは引数として指定された文字列からパス名部分を取り除く関数です. たとえば(file-name-nondirectory "/home/hatai/abc.txt")とすると, abc.txtという文字列が帰ってきます(UNIXのbasenameコマンドと同じ動作).

しかし,このままでは~/.bak/ディレクトリが存在しない場合にバックアップファイルの作成が失敗してしまいまうという問題があります. [注:正確には失敗するわけではなくて%backup%~のようなファイル名でバックアップされますが, いずれにしても期待通りの動作にはなりません.] 次の例では~/.bak/というディレクトリがあるかどうかを調べて, あればそのディレクトリの下にバックアップファイルをつくり, なければデフォルトと同じようにファイル名の末尾にチルダ(~)をつけたものをバックアップのファイル名とします. [補足:~/.bakがディレクトリではなく普通のファイルだった場合もデフォルトと同じ動作にしないといけないので, file-exists-pではなくfile-directory-pを使って判定するのがよいと思います.]

(defun make-backup-file-name (filename)
  (if (file-directory-p "~/.bak")
      (concat "~/.bak/" (file-name-nondirectory filename))
    (concat filename "~")))

これでとりあえず穴はなくなったのですけど, このプログラムではデフォルトの動作=(concat filename "~")だと決めつけてしまっているところがちょっと気になります. 現時点でのファイル名の末尾にチルダをつけるというデフォルトの動作がいつまでも変わらない保証はないし, サイトごとの設定ファイルなどでデフォルトの動作が既に入れ替えてある可能性もあります. そこで,ホントのデフォルトの動作=make-backup-file-nameを再定義する前の定義内容を make-backup-file-name-originalというシンボルに控えておき, デフォルトの動作をさせたいところでは これを使うようにします.

(fset 'make-backup-file-name-original
      (symbol-function 'make-backup-file-name))
(defun make-backup-file-name (filename)
  (if (file-directory-p "~/.bak")
      (concat "~/.bak/" (file-name-nondirectory filename))
    (make-backup-file-name-original filename)))

このプログラムが複数回評価されるかもしれないことを考慮すると, せっかくmake-backup-file-name-originalに控えておいたデフォルトの動作が 2回目以降の評価で失われてしまう点が気になります. [たとえば.emacsを書き換えた際に,書き換えた内容に文法エラーがないかどうかを調べたり, 書き換えた内容をすぐに反映したりするために, M-x eval-bufferを実行して.emacs全体を再評価するというような操作をすることがあると思います. このような場合にせっかく控えておいたデフォルト動作が上書きされてなくなってしまいます.] そこで,既にmake-backup-file-name-originalに関数がバインドされていたら, 一連の式を再評価しないようにして,この問題を回避します(C言語のヘッダーファイルの複数回インクルード問題を回避する方法と同じ考え方です).

(if (not (fboundp 'make-backup-file-name-original))
    (progn
      (fset 'make-backup-file-name-original
            (symbol-function 'make-backup-file-name))
      (defun make-backup-file-name (filename)
        (if (file-directory-p "~/.bak")
            (concat "~/.bak/" (file-name-nondirectory filename))
          (make-backup-file-name-original filename)))))

あとは,バックアップを保存するディレクトリを簡単に変更できるように, バックアップディレクトリはシンボルbackup-directoryに設定されている値を使うようにします. 同時にbackup-directoryが指定されていない場合は,そもそもデフォルトの動作でOKという意味なんだろうなと解釈して, make-backup-file-nameの再定義自体を行わないようにしました.

(setq backup-directory "~/.bak")
(if (and (boundp 'backup-directory)
         (not (fboundp 'make-backup-file-name-original)))
    (progn
      (fset 'make-backup-file-name-original
            (symbol-function 'make-backup-file-name))
      (defun make-backup-file-name (filename)
        (if (file-directory-p backup-directory)
            (concat backup-directory "/" (file-name-nondirectory filename))
          (make-backup-file-name-original filename)))))

以上で完成です. [完全版では,さらにbackup-directoryを参照するところで (expand-file-name backup-directory)としてフルパスに展開していますが, これはホントに不要(冗長)のはずですので書かなくてOKと思います.] [注:cygwin版のEmacsでは,チルダを含むパス(例えば~/.bak)の展開がうまくいかないらしく, このプログラムはうまく動かないようです. いろいろやってみましたけど,今のところ解決法が見つかっていません….]

参考

[1] GNU Emacs Lisp Reference Manual - Backup Names

はたいたかし
2005-09-13
Home > Dev > Emacs