🎃

サーバーの設定ファイルを保存したときにバックアップを取るようにする

に公開

たまたま、twitterで下記のような投稿を見つけた。

https://x.com/tamagotake3454/status/1910831541904957859

設定を変更する前にバックアップを取るというのは良い習慣だとは思うが、
手動バックアップである以上、常に人がミスをする可能性がある。

よって、
「サーバーの設定ファイルが自動バックアップされているようになっている。サーバーの設定ファイルが自動バックアップされるように設定を行う」

というふうにするべきだと思う。

大抵のオンプレサーバー、VPNだとvimがインストールされていると思うので、
下記のように~/.vimrcに追記すると、自動的にバックアップが取られるようになる。

~/.vimrc
" バックアップディレクトリ設定
set backup
set backupdir=~/.vim/backup//
" ディレクトリがなければ作成
if !isdirectory(expand('~/.vim/backup'))
  call mkdir(expand('~/.vim/backup'), 'p')
endif

" 上書き時に日付付きバックアップを作成
autocmd BufWritePre * call s:backup_current_file()
function! s:backup_current_file()
  let l:filename = expand('%:p')
  if l:filename !~# 'COMMIT_EDITMSG' && l:filename !~# '\.git' && filereadable(l:filename)
    " /がディレクトリの区切り文字と被るので、/を__とリプレイスする。
    let l:backupname = expand('~/.vim/backup/') . substitute(l:filename, '/', '__', 'g') . strftime('@%Y%m%d%H%M%S')
    silent execute '!cp ' . l:filename . ' ' . l:backupname
    " バックアップファイルが書き込みできると不味いので、読み取り専用にする。
    silent execute '!chmod 440' . l:backupname
  endif
endfunction

当方、emacsは全く触ったとこがないので、emacsの人は各種それっぽいスクリプトに置き換えて考えてください。

この設定をした後でたとえば、/home/katsutoshi/helloworldというファイルがあると
そのファイルを上書きした後に自動的に

/home/katsutoshi/.vim/backup/__home__katsutoshi__helloworld@YYYYmmddHHMMSS
というファイルが作成される。
このファイルに保存前の内容が入っているのでこれを見ると良い。

重要な処理としては次のとおりになる。

  1. /がディレクトリの区切り文字と被るので、バックアップファイルのフルパスに使われる/を__とリプレイスする。(vimのバックアップファイルはデフォルトではディレクトリは%区切りだが、bashなどで処理するときに%はパラメータ展開などの特殊文字として評価されるので自作するバックアップ処理に使いたくない。)
  2. バックアップファイル名に日時を追加
  3. バックアップファイルが編集されないように所有者とグループのみ読み取り可能とする。

これで運用していくと~/.vim/backupディレクトリは次のように
set backupで作成されるデフォルトのバックアップファイルと、バックアップスクリプトによっての日時も入れた履歴込みのバックアップファイル両方が
混在する。

ls -l ~/.vim/backup/
total 16
-rw-r-x--- 1 katsutoshi katsutoshi 108 Apr 13 17:20 %home%katsutoshi%abc~
-r--r----- 1 katsutoshi katsutoshi 103 Apr 13 17:20 __home__katsutoshi__abc@20250413172026
-r--r----- 1 katsutoshi katsutoshi 106 Apr 13 17:20 __home__katsutoshi__abc@20250413172030
-r--r----- 1 katsutoshi katsutoshi 108 Apr 13 17:20 __home__katsutoshi__abc@20250413172035

年月ごとにバックアップを分けたい。

ファイルが増えてきたり、昔の日時のファイルを探すと大変、
また、 set backupで作成されるデフォルトのバックアップファイルと同じ階層のディレクトリなのもなんか気持ち悪い。
よって、バックアップを年月ごとのフォルダに入るようにしたい。

~/.vimrc
" バックアップディレクトリ設定
set backup
set backupdir=~/.vim/backup//
" ディレクトリがなければ作成
if !isdirectory(expand('~/.vim/backup'))
  call mkdir(expand('~/.vim/backup'), 'p')
endif

" 上書き時に日付付きバックアップを作成
autocmd BufWritePre * call s:backup_current_file()
function! s:backup_current_file()
  let l:filename = expand('%:p')
  if l:filename !~# 'COMMIT_EDITMSG' && l:filename !~# '\.git' && filereadable(l:filename)
    " 日付ディレクトリ (例: ~/.vim/backup/202504)
    let l:date = strftime('%Y%m')
    let l:dated_backupdir = expand('~/.vim/backup/' . l:date)

    " ディレクトリがなければ作成
    if !isdirectory(l:dated_backupdir)
      call mkdir(l:dated_backupdir, 'p')
    endif

    " /がディレクトリの区切り文字と被るので、/を__とリプレイスする。
    let l:backupname = l:dated_backupdir . '/' . substitute(l:filename, '/', '__', 'g') . strftime('@%Y%m%d%H%M%S')
    silent execute '!cp ' . l:filename . ' ' . l:backupname
    " バックアップファイルが書き込みできると不味いので、読み取り専用にする。
    silent execute '!chmod 440' . l:backupname
  endif
endfunction

上のようなファイルを書くと、
下記のように自動的に年月ごとにディレクトリを作成してバックアップと、
直前のバックアップというふうにわかりやすい構成になる。

ls -l ~/.vim/backup/
total 8
-rw-r-x--- 1 katsutoshi katsutoshi  113 Apr 13 18:41 %home%katsutoshi%abc~
drwxr-xr-x 2 katsutoshi katsutoshi 4096 Apr 13 18:45 202504

注意事項

今回紹介したスクリプトはvimのみ動作します。
neovimの場合は設定ファイルの位置が異な向かないと思う。
neovimの場合は~/.vimrc出なくて、~/.config/nvim/init.vimとなる。
(そもそもneovimはvimと比べて互換性を壊していく方針なので、サーバー用途に向かないと思う。rhelもレポジトリとして提供されているのはneovimでなくてvimだし。)

Discussion