サーバー上の設定ファイルを更新しようと cp を実行した直後、「元のファイルを確認しておけばよかった」と気づく場面は珍しくありません。デフォルトの cp は確認なしで既存ファイルを上書きするため、元に戻す手段がその場で消えます。cp にはコピー先の既存ファイルを自動で退避してから書き込むオプションが用意されており、使い方を知っておくだけで上書き事故のリスクを大幅に下げられます。
バックアップ形式の選択肢を把握する
cp で既存ファイルを保護する手段は主に三系統あります。状況に応じて使い分けてください。
- チルダ後置形式(
-b):既存ファイルのファイル名末尾に~を付けて退避。直前の1世代のみ保持。 - 番号付き形式(
--backup=numbered):.~1~、.~2~のように世代番号を付けて退避。繰り返しコピーしても全世代が残る。 - 上書き禁止(
-n)または対話確認(-i):バックアップを作らず、既存ファイルへの書き込み自体を止める。
チルダ付きバックアップで直前の状態をすぐ取り出せるようにする
最も手軽なのが -b オプションです。コピー先に同名ファイルがある場合、そのファイルを ファイル名~ という名前に変更してから新しいファイルを配置します。
# コピー先の状態を確認
$ ls -l /etc/myapp/
-rw-r--r-- 1 root root 1024 Jul 1 10:00 app.conf
# -b を付けてコピー(コピー先に同名ファイルがある場合に退避が発生)
$ cp -b app.conf /etc/myapp/
# コピー後の確認
$ ls -l /etc/myapp/
-rw-r--r-- 1 root root 2048 Jul 9 09:30 app.conf # 新しいファイル
-rw-r--r-- 1 root root 1024 Jul 1 10:00 app.conf~ # 退避された旧ファイル
元のファイルに戻すには mv /etc/myapp/app.conf~ /etc/myapp/app.conf を実行するだけです。バックアップのファイル名後置文字列は --suffix で変更できます。.bak が好みであれば次のように指定します。
# バックアップの拡張子を .bak にする
$ cp -b --suffix=.bak app.conf /etc/myapp/
# → app.conf.bak として退避される
注意点として、-b が保持するのは直前の1世代のみです。同じコマンドを2回実行すると、1回目に作成した app.conf~ が2回目の実行で上書きされます。「直前の状態に1回だけ戻れれば十分」という用途に向いています。
番号付きバックアップで複数の変更履歴を残す
設定チューニングを繰り返す作業では、途中のどの状態にでも戻れるよう世代を蓄積したくなります。--backup=numbered を使うと、コピーのたびにバックアップが番号付きで積み上がります。
# 1回目のコピー
$ cp --backup=numbered app.conf /etc/myapp/
$ ls /etc/myapp/
app.conf app.conf.~1~
# 設定を変更してもう一度コピー
$ cp --backup=numbered app.conf /etc/myapp/
$ ls /etc/myapp/
app.conf app.conf.~1~ app.conf.~2~
# 最初の状態(.~1~)に戻したい場合
$ cp /etc/myapp/app.conf.~1~ /etc/myapp/app.conf
番号付きバックアップは自動では削除されません。頻繁に実行する環境では古い世代が際限なく蓄積されるため、運用ルールとして世代数の上限を決めておくか、定期的に古いバックアップを整理する仕組みをあわせて用意してください。ある運用現場では、デプロイスクリプトに番号付きバックアップを組み込んだところ、半年で数百ファイルが溜まりディスク逼迫の原因になった事例があります。
既存ファイルへの書き込み自体を止める
「バックアップを取るより、そもそも既存ファイルを触らせない」という判断が適切な場面もあります。スクリプトで雛形ファイルを初期配布する処理など、「ファイルがなければ配置、あればスキップ」というロジックに使えます。
# コピー先に既存ファイルがある場合はスキップ(サイレント)
$ cp -n app.conf /etc/myapp/
# コピー前に確認プロンプトを出す(対話端末向け)
$ cp -i app.conf /etc/myapp/
cp: overwrite '/etc/myapp/app.conf'?
-n はコピーをスキップしても終了コードが 0 を返すため、スクリプト内で「コピーが実行されたかどうか」を終了コードで判定することはできません。配置の成否を後続処理で確認したい場合は、test -f で事前にファイルの存在を確認するか、コピー後にファイルのタイムスタンプや内容を比較する方法を組み合わせてください。なお、-n と -b を同時に指定した場合は -n が優先され、バックアップは作成されません。
対話端末での安全策をエイリアスで標準化する
作業端末では cp コマンド自体を対話確認付きにしておくと、うっかり上書きへの安全網になります。~/.bashrc に次の1行を追記してください。
# ~/.bashrc に追記
alias cp='cp -i'
# 設定を即時反映
source ~/.bashrc
多くのディストリビューションでは root の /root/.bashrc にすでにこのエイリアスが記述されています。ここで重要な落とし穴があります。エイリアスは対話シェル(インタラクティブシェル)でのみ展開されるため、シェルスクリプト内の cp には効きません。一方、スクリプトを root で実行する場合、環境によっては /root/.bashrc の alias cp='cp -i' が読み込まれてしまい、スクリプトが標準入力待ちになって処理が止まる事象が発生します。スクリプト内では /bin/cp とフルパスで指定するか、\\cp(バックスラッシュでエイリアスを無効化)を使うと確実です。
#!/bin/bash
# スクリプト内では以下のどちらかを使う
# フルパス指定(エイリアスを無視)
/bin/cp -b app.conf /etc/myapp/
# バックスラッシュでエイリアスを無効化
\cp -b app.conf /etc/myapp/
操作前に確認しておくべきポイント
-bのバックアップは直前の1世代のみ。同じファイルに繰り返し使うと前回のバックアップが上書きされる。--backup=numberedのバックアップは自動削除されない。定期的な整理ルールを決めておく。-nと-bを同時指定すると-nが優先される(バックアップは作成されない)。-nはスキップしても終了コード0を返す。スクリプト内での成否判定には使えない。- スクリプト内では
/bin/cpまたはバックスラッシュ付きでエイリアスの影響を排除する。 - バックアップファイルはコピー先ディレクトリに作成される。リモートマウントや容量が限られたパーティションへのコピーでは残容量に注意する。
「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。
ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。
>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)
※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。
