ファイル入れ替えにおいてmvは使用してはいけない
サーバにローカル端末で更新した設定ファイル等をアップしてからmvで所定のファイルに移動させると、権限や所有者が変わってしまうことがある。
cpコマンドを--preserve
オプションや--no-preserve
オプションをつけずに実行し、上書きすると、timestampは更新されるものの、権限や所有者は元のファイルのまま保たれる。
つまり、ファイル入れ替えにおいてmvは使用してはいけない。
サンプルを用いてmv, cpをファイルの入れ替えでどのように使うべきか、また、なぜ権限等が変わってしまうのかを記載する。
mvではなくcpを使う
/usr/local/bin/test.shを~/test.shと入れ替える、という例を題にmvではなくcpを使う方法を書く。
ORIGINAL=/usr/local/bin/test.sh
UPDATED=~/test.sh
BACKUP=/tmp/test.sh
NGパターン
cp -p $ORIGINAL $BACKUP
mv -f $UPDATED $ORIGINAL
OKパターン
cp -p $ORIGINAL $BACKUP
\cp -f $UPDATED $ORIGINAL
# \cpでcpのalias(cp -i)が使われないようにし、-fオプションが有効になるようにしている。
特別に何かほかの情報を更新したい、あるいは更新したくないという場合は、--preserve
オプション、--no-preserve
オプションで対応すればいい。
--preserve
オプションで指定できる項目
項目 | --preserv e=に指定するATTRIBUTE |
---|---|
最終更新日時と最新アクセス日時 | timestamps |
所有者と所有グループ | ownership |
アクセス権(パーミッション) | mode |
SELinuxの「コンテキスト」と呼ばれる情報 | context |
ディレクトリ内に存在するハードリンク | links |
ファイルシステムの拡張属性 | xattr |
参考:cpコマンドでファイルやディレクトリをコピーした際に保持される情報/属性について
それでもmvを使いたければ
cpではなくmvを使う場合、無理やり権限等を正そうとすると、chmodとchownの--reference
オプションを使って権限やオーナー・グループを元のファイルからコピーする必要がある。
# backup
cp -p $ORIGINAL $BACKUP
# 権限をコピー
chmod --reference=$ORIGINAL $UPDATED
# オーナー・グループをコピー
chown --reference=$ORIGINAL $UPDATED
# 入れ替え
mv -f $UPDATED $ORIGINAL
mvで権限等が変わる理由
そもそもmvで権限等が変わる理由は、変更元と変更先のファイルパスが同一ファイルシステム上にある場合という条件付きではあるが、mvコマンドがrename()というファイル名を変更するシステムコールを利用しているからだ。
manによると、renameは、変更後のファイルnewpathがすでに存在するときには、ファイルoldpathがファイルnewpathをatomicに置き換える。
置き換えなので権限等もoldpathのもの(今回の例で言うと$UPDATED)がそのまま持ち越される。
cpで権限等が変わらない理由
cpコマンドはopen()というファイルを開くシステムコールを利用している。
コピー先のファイルが既に存在しているなら、 open(path, O_WRONLY | O_TRUNC)
でファイルを開く。既存のファイルを書き込みモード開いて、長さ 0 に切り詰め(O_TRUNC)、コピー元のファイルの内容を書いていくので、権限等が変わる要素がない。
ただし、cp -f
として-f
オプションをつけて実行したとき、既存のファイルの書き込みモードでのオープンに失敗すると、cpは既存のファイルの削除 (もしくはアンリンク) を試み、削除に成功した場合は新規ファイルへのコピーとなる。サーバメンテナンスでファイルの入れ替え時に既存ファイルが開けない状況は(サービス等を一旦停止しているため)無いと思うが、cpの仕様として把握しておいた方がいいだろう。