MacのデフォルトEDITOR
MacのデフォルトのEDITORは/usr/bin/vi
に設定されています(少なくとも今のMacでは)
1。
ただし、これはvi
になってますが実態はvim
です。
なのでcrontab -e
などとしてエディタを呼ぶプロセスを立ち上げると
vim
を呼んで編集作業に入ります。
.bashrc
なんかに
export EDITOR=vi
等と書けばどこの環境でもデフォルトEDITORが上書きされてこれが使われる様になります。
crontab -e
での編集
crontab -e
でcronジョブを編集する際、Vim等で開くのは実際の設定ファイルではなく、
それのコピーを作って編集しcrontab
がオリジナルファイルをアップデートする、
と言う形になっています。
オリジナルの設定ファイルはMacでは
/usr/lib/cron/tabs/USER
という様にユーザ名で保存されています 2 。 このファイル(tabsディレクトリ自体も)は管理者のみが見れる様になっていて、 無理やり見てみると
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (/tmp/crontab.pP2i90rWvI installed on Thu Jun XX XX:XX:XX 2014)
# (Cron version -- $FreeBSD: src/usr.sbin/cron/crontab/crontab.c,v 1.24 2006/09/03 17:52:19 ru Exp $)
...
の様な感じで、編集しないでね、と言う文言が設定ファイルの最初に付け加えられています。
crontab -e
とすると、まず、/private/tmp/crontab.XXXXXXXXXX
(XXXXXXXXXX
はランダムな文字列)
というファイルを開いてそれを開きます。
3。
:write
等で保存すると、これらのテンポラリーなファイルがアップデートされるだけで、
オリジナルはそのまま。
最後に編集を終えて:quit
した後にcrontab
が内容をオリジナルの方へ持っていく
形になっています。
Vimのバックアップ機能
問題はVimのバックアップ機能との兼ね合いにあります。
Vimでは編集を始めるとき、通常バックアップを作っていて、その作り方によってこの様な 作業に影響が出ることがあります。
Vimにはbackupcopy
(bkc
)という設定項目があって、この値が
- yes: コピーを作ってオリジナルのものを上書きする
- Pros: リンクだとか所有者だとかのファイルのプロパティがそのまま保たれる。
- Cons: コピーを作る分ちょっと時間がかかる。ファイルがリンクの時はバックアップ名がリンクの名前になる。
- no: ファイルの名前を書いて新しいファイルに書く
- Pros: 速い。
- Cons: ファイルのプロパティが違うものになる場合がある。ファイルがリンクの場合、新たなファイルはリンクでなくなる。
- auto: 適宜どちらかを選ぶ
となります。 yesとnoでは
これだとよくわからないので実際にやってみます。
バックアップを作る設定でbackupcopy
をyes
にしてみます。
1 2 3 |
|
$ touch test.txt
$ ls -i
10393293 test.txt
$ vim test.txt # and do at least ":write"
$ ls -i
10393293 test.txt 10393552 test.txt~
$ vim test.txt # and do at least ":write"
$ ls -i
10393293 test.txt 10393584 test.txt~
とバックアップファイルのinodeナンバーが変わっていきます。
(他のターミナルからVimを立ち上げて:w
とするだけでも変わるのでそれで簡単に確認できます。)
今度はno
にしてみます。
1 2 3 |
|
$ rm -f test.txt*
$ touch test.txt
$ ls -i
10394581 test.txt
$ vim test.txt # and do at least ":write"
$ ls -i
10394782 test.txt 10394581 test.txt~
$ vim test.txt # and do at least ":write"
$ ls -i
10394919 test.txt 10394782 test.txt~
と、今度は元々のファイルのinodeナンバーを持ったバックアップファイルが出来て、 元々のファイルのところには新しいinodeナンバーを持った物が出来ている事が分かります。
この場合はauto
で行うと、no
の方と同じ結果になりました。
さらにリンクの場合、yes
だと
$ rm -f test.txt*
$ touch test.txt
$ ln -s test.txt zzz.txt
$ ls -i
10401021 test.txt 10401335 zzz.txt
$ vim test.txt # and do at least ":write"
$ ls -i
10401021 test.txt 10401335 zzz.txt 10401721 zzz.txt~
$ ls -l
... test.txt
... zzz.txt -> test.txt
... zzz.txt~
となり、新しいバックアップファイルが追加されただけの形。
一方、no
だと
$ rm -f *.txt*
$ touch test.txt
$ ln -s test.txt zzz.txt
$ ls -i
10402011 test.txt 10402334 zzz.txt
$ vim test.txt # and do at least ":write"
$ ls -i
10402011 test.txt 10402631 zzz.txt 10402234 zzz.txt~
$ ls -l
... test.txt
... zzz.txt
... zzz.txt~ -> test.txt
となり、リンクだったzzz.txt
は実態ファイルになり、
リンクはバックアップの方に移ってしまいます
4。
こちらはファイル構造自体を崩してしまうので、
シンボリックリンクがある環境で編集するときにset backupcopy=no
にしておくのは
ちょっとむずかしいと思います。
この、リンクに対する物をauto
でやってみると
yes
と同じ様になり、確かに適宜良い方を選んでる感じです。
デフォルトではbackupcopy
はauto
になってるので、
普段使いでは問題なく、速く出来るところは速く行える様に適宜選択されている様です。
crontab -e
の編集でVimを使う時の問題点
この様なVimの仕様があるので、
crontab
の様なコマンドの中で使われるときには問題が起こる様です。
Vimで:help crontab
にも書いてありますが、
問題が起こるのは上のbackupcopy
の値がno
の時です。
auto
にしておいても、crontab
の中でVimが開くファイル自体は一度コピーされた
通常ファイルなので、上でやったようにno
と同じ動きをするので問題になります。
原因としては上にある様に、本来crontab
がコピーしてきたものが
Vimによって名前を変えられ、同じ名前のものとして新しいファイルを作るので、
所有者などのファイルのプロパティが変わってしまい正しく処理出来なくなります。
バックアップを作る設定でbackupcopy
をno
やauto
にしていると
crontab -e
で編集し終わって終了するときに
crontab: temp file must be edited in place
と言われ、変更が反映されずに終わってしまいます。
(さらに/private/tmp/
にcrontab.XXXXXXXXXX
が残る。。。)
対処法
対処法としては、
backupcopy
を常にyes
にする- バックアップを作らない
の二つが考えられます。
どちらの場合も、すべての場面で適用してしまうとせっかく賢い機能が
ごくたまにしか使わないcrontab
だけのために失われてしまうので
失われてしまうので、BufRead
などを使って、
autocmd BufRead /private/tmp/crontab.* set backupcopy=yes
とすれば5、
crontab
の時だけ強制的にバックアップをコピーで作るように出来ます
6。
バックアップ自体をしない場合には
autocmd BufRead /private/tmp/crontab.* set nobackup nowritebackup
の様に、backup
とwritebackup
の方もno
に設定しておきます
7
。
また、もう一つbackupskip
というオプションがあって、に関するオプションがあって、
これを
set backupskip=/private/tmp/crontab.*
とすると、これに該当するファイルを開くときにはバックアップをしなくなります。
このオプションがある意味を考えるとBufRead
でバックアップをオフにするよりは
これを使うべきかも。
ただ、backupskip
には予め/tmp/*
等幾つかの値が含まれている
(Windows環境とLinuxで違うものだったり環境を考慮したものにもなってる)ので、
これを残すために
let &backupskip="/private/tmp/*," . &backupskip
の様let
を使って/private/tmp/*
を加えて上げる方が良いと思います。
追記: 2017/05/30
let
をわざわざ使わなくても
set backupskip+=/private/tmp/*
と+=
することでフォルダを付け足すことが出来るのでこちらを使った方が良いです。
(コメントありがとうございます。)
追記ここまで
この他にもバックアップのディレクトリ指定のbackupdir
を空に(set backupdir=
)
するとこの場合もバックアップは無効になります。
GNU版、BSD版の違い
Macでは余りcrontab
を使わないので、
以前の環境ではvimの設定をする前に書いたのか問題なく書けたて気がしますが、
途中からアップデートしたものとか実は有効でなかったのかもしれません。
(dotfiles等のGitレポジトリののアップデートくらいでしかしてない上、
普段少し変更するとコメントを残すためになるべく手でcommit
& push
してたので
確認せずに気づかなかっただけかも。。。)
ただ、Linux環境ではよく使っていて、きちんと動いてることも確認していますし、
ちょっとやってみたら同じ.vimrc
でも問題なかったので少し違いを見てみました。
まず、cron
コマンド自体がGNU版、BSD版があるのでそれぞれで微妙に違います。
- 設定ファイルの場所
- BSD:
/usr/lib/cron/tabs/USER
- GNU:
/var/spool/cron/crontab/USER
- BSD:
- GNU版の方には
-i
(-r
時にy/Y
と確認するようになる)、-s
(SELinux様セキュリティ設定?) のオプションがある。
みたいな感じ。この辺りは普段それほど気にするところでは無いと思いますが、
注釈等にも書いてあるように、Linux環境ではテンポラリーなファイルが
/tmp/crontab.XXXXXXXXXX
に作られます。
Macと違ってこちらの/tmp
は通常ディレクトリなので、backupskip
での指定も
/tmp/*
となってればスキップするようになります。
Vimでのデフォルトに/tmp/*
が含まれているので、これで問題はない、ということも言えます。
ただ、気になってこれを敢えて外して(set backupdir=
と書いて)試してみたところ
それでも問題なくcrontab -e
で更新出来ました。
色々いじってみて気づいた違いとして、backupを取らない様な状態で、
- BSD:
:wq
と保存して終了した場合でも、元のファイルとの違いがなければcrontab: no changes made to crontab
- GNU: 元のファイルとの違いが無くても
:wq
と保存して終了するとcrontab: installing new crontab
と、少し違いがあります。BSDの方は一度ファイルを保存しても その後ファイル比較を行って変更がなければcronの本来のファイルを変更しないみたいですが、 GNUの方は有無を言わず上書きしています。
さらに、GNU(Linux)の方でset backup
とset backupcopy=no
を設定して、
set backupskip=
として必ずバックアップを作るようにしても
問題なくアップデート出来ました。
(/tmp
を見るときちんとバックアップが残っています。)
なので問題が起こるのは、BSDの方では、ファイルの中身やプロパティをきちんと比較しようとして いるために問題が起こる一方、 GNU版では変更は特にチェックせずにテンポラリファイルが一度でも保存されたら 単に中身だけをコピーする、みたいな事をしているから問題が起こらない、という感じです。
-
Linuxなんかだと大概
ed
コマンド。 ↩ -
Linux(GNU版?)だと
/var/spool/cron/crontab/USER
) ↩ -
Linux(GNU版cron?)だと
/tmp/crontab.XXXXXXXXXX
。 Macでも/tmp
が/private/tmp
へのシンボリックリンクになっているので/tmp/crontab.XXXXXXXXXX
にも見えます。 ↩ この場合もバックアップファイルの名前はリンクの名前になるので、 上の
yes
の場合の悪い点としてファイルの名前の事をあげてる意味がよくわかりません。。。 ↩-
ここで
/private/tmp
でなくて、/tmp/crontab.*
でも大丈夫です。 ちょっと良く理解してませんが、BufRead
やBufEnter
で使う際には どちらでも有効になりました。ただ、下に書いてある様に
backupskip
の方の設定ではprivate
の方しか効きませんでした。 ↩ -
crontab
時に他のファイルを参照することもあるかもしれないので、setlocal
を使ってる例なんかもありましたが、backupcopy
はバッファ毎には出来ない設定のようで、setlocal
を使っても グローバルな設定になるみたいです。バッファ毎に出来るものと出来ないものがある事自体初めて知りました:
Vim:
:h option-summary
-
backup
が設定されてる時はwritebackup
の値にかかわらず、 ファイルを保存するときに常に バックアップが作られ、保存後も残ります。 古いバックアップファイルがある場合は消してから新たなバックアップファイルを作ります。backup
がnobackup
に設定され、writebackup
が設定されてる時には、 ファイルを保存する時にはバックアップが作られますが、 保存が成功するとそのバックアップファイルは消されます。 (なのでよほどサイズの大きいファイルを保存しようとして時間がかかったりしない限り~
ファイルは見えません。) また、この場合にも、古いバックアップファイルがある場合には先にそれを消し、 後には残らない様になります。nobackup
かつnowritebackup
が設定されているときはこのバックアップは行われません。一方で、
swapfile
オプションというのがあって、 こちらは編集中常にファイルの変更を監視しながらswapファイル(バイナリファイル)を作って、 クラッシュ時等にそれを元に復活出来る事があります。swapfile
を有効にしていればwritebackup
だけ有効にしておいても あまり意味が無い気もします。。。