オフィシャルドキュメント: GNU tar Reference Manual (FSF)

tar

tar はあまりにも多くの機能を持ち、全てを網羅することはできないし、しても仕方がないので、ここでは、安上がりで簡単にバックアップを取るひとつの方法を紹介する。

バックアップにおける tar のメリットは、なんと言っても、UNIX のファイル/ディレクトリ構造を忠実に保持できることだ。例えば、 Linux 上のファイルを直接 CD に焼いたら、UNIX ファイル属性を保持できない ISO9660 ファイルシステム上に書かれることになるので、リストアの際にはいちいちパーミションやオーナーを設定し直してやらなければならない。また、tar はアーカイブすると同時に gzip, bzip2 で圧縮を掛けることも可能なので、バックアップメディアも節約できる。さらに、 tar の付加機能のひとつであるインクリメンタル (増分) バックアップを利用すれば、前回の時から更新、追加されたファイルやディレクトリだけをバックアップすることもできる。

Table of Contents

tar によるお手軽バックアップ

TAR という名前は元々 Tape ARchive から来ているというくらいで、デバイス (テープや MO) 自体をひとつのファイルのように扱って書き出し先とすることもできるが、ここでは、お手軽を旨とし、以下のような方針でバックアップを行う:

tarの使い方

基本

/home/hoge/ にある file1, dir2 をひとつのアーカイブ hoge.tar にまとめるには、下のようにコマンドする:

tar -C / -cf hoge.tar  home/hoge/file1 home/hoge/dir2

gzip 圧縮も掛けたければ:

tar -C / -czf hoge.tar  home/hoge/file1 home/hoge/dir2

ファイル引数 (後ろのふたつ) の頭にルートディレクトリを示す / が欠けていることは結構重要。付けたとしても tar が自動的に (文句を垂れながら) 取り払う。これは tar がディレクトリ構造を含めてデータを保存するということと密接に関係している。もしも、アーカイブ内に保存されてる情報が
/home/hoge/file1
/home/hoge/dir2
だったら file1dir2 は厳密に元あった場所にしか復活させられない。しかし、実際の動作では
home/hoge/file1
home/hoge/dir2
という具合に相対指定なので、例えば今 /tmp/ に居れば、
/tmp/home/hogefile1dir2/ が得られる。こういう動作を殺す --absolute-names というオプションもあるが、解凍に柔軟性が全くなくなるので、よほどの理由でなければ使ってはいけない。

tar のオプション (ごく一部)

主操作系
-c, --create 書庫の作成
-x, --extract 解凍する。圧縮書庫なら -z-j も必要
-t, --list 書庫の中身をリストする。圧縮書庫なら -z-j も必要
全ての主操作で共通
-f, --file= 操作対象とする書庫ファイルを指定
-v, --verbose 進捗やエラーを詳細に表示
-z, --gzip zgip 圧縮/解凍を行う
-j, --bzip2 bzip2 圧縮/解凍を行う
-C <dir>,
--directory=<dir>
dircd する。書庫作成時の使用では、他の引数との順序が問題となるので、基本的にはファイル名より前に置いたほうがいい。というのも、このオプションはひとつのコマンド中で何回も使え、例えば:
tar -cf a.tar -C /home/dir1 file1 -C ../dir2 file2
と (こういう風に次々に相対的に移動することも可能) すれば、 /home/dir1/ にある file1/home/dir2/ にある file2 が書庫に保存される。それに対してあらかじめ / に cd しておいてから:
tar -cf a.tar home/dir1/file1 home/dir2/file2
した時とでは、書庫内に保存されるオブジェクトが異なる。前者では単なる file1file2 が書庫内に存在する結果となるが、後者で保存されるオブジェクトは
home/dir1/file1
home/dir2/file2
となり、当然ながら解凍した時の結果が異なる。
-g <file>,
--listed-incremental=<file>
増分メカニズムを使用する。file はディレクトリの概況を貯える管理ファイル。書庫自体に概況も記録する -G オプションも下位互換性のために残されているが、本稿では触れない。
-M,
--multi-volume
メディアがいっぱいになったら次のメディアへ分割する (マルチボリューム動作)。 --tape-length=<num> を併記すれば、アーカイブ先がファイルの場合でも利用可能。後述
--exclude=<pattern> 入力から除外するファイルやディレクトリ。シェルグロブパターン ( *, ?, [ ], [! ], [^ ] ) が使える。 tar のドキュメントでは、パターン文字列が tar に届く前にシェルによって展開されてしまわないよう、 <pattern> はシングルクォートで囲んだほうがよい、とされているが、私の環境では逆に、クォートすると正常に除外してくれなかった。不確実なので、下の -X を使ったほうが確実。
-X <file>,
--exclude-from=<file>
上述の除外グロブパターンを <file> から読み込む。パターンは 1 行にひとつずつ書いておく。
--ignore-failed-read 読めないあるいは存在しないファイルがあっても処理を続行。リターンコードも常に 0 (正常) を示す。
書庫作成系
--no-recursion ディレクトリを再帰的に辿らない。例えば
-cf /some/dir
で使うと書庫上には空の /some/dir が保存されるだけ。
-cf /some/dir/*
の場合は /some/dir 直下のファイルと、直下のディレクトリ (空) ができる。
--owner=<user> 書庫のファイルやディレクトリの所有者を <user> にする。別のマシン上でアーカイブを解凍する場合、書庫を作成したマシン上と同じユーザ ID が存在するとは限らないわけだが、例えば root 所有つまり --owner=0 にして書庫を作成しておけば、その問題を回避できる。ただし、解凍の際には、もちろん、そのユーザビットを操作できる権限が必要で、そうでなければ解凍実行者の ID が適用される。
--group=<group> 上記のグループ版
解凍系
-p,
--preserve-permissions
パーミションビットを忠実に再現する。通常動作では、書庫内に記録されたディレクトリの umask に左右される。
-O, --to-stdout ファイルを実体として解凍する代わりに、そのファイルの内容を stdout に出力。内容を確認したり、他のプログラムにパイプ渡しする時に便利。

というわけで、バックアップという用途で、また cron から実行させることを考慮すると、下記のようなコマンドが適切と思われる:

tar -C / --ignore-failed-read --exclude=*~ -czf hoge.tar \
  home/hoge/file1 home/hoge/file2 home/hoge/dir1

--exclude=*~ は、エディタが残すバックアップファイルを除外するため。

インクリメンタルバックアップ

GNU tar ではインクリメンタル (増分) バックアップというメカニズムも利用できる。これを使うと、前回のバックアップから更新/追加されたファイルやフォルダだけを書庫に書き出すとともに、ディレクトリの概況 (snap-shot) が記録される。この「概況」は、オプション -g <file> で指定したスナップショットファイルに保存される。そして、リストアの際に同じく -g オプションでそのスナップショットファイルを指定すれば、更新/追加されたファイルやフォルダが解凍されるだけでなく、スナップショットで「存在しないことになっている」ファイルやフォルダは、リストア先ファイルシステム上から削除してくれる。つまりバックアップを行った時点での状態が完全に再現されるわけだ。

ややこしいので例を挙げよう。ここに、以下のような構成のフォルダがあるとする:

/home/tmp/
         1.txt
         2.txt

まずここで、第一世代のバックアップを取る:

tar -g /var/test.snar -C / -cf /var/test_0.tar  home/tmp

この時取れたバックアップ test_0.tar には上記の全ての構成メンバーがアーカイブされている。作業時点ではまだスナップショットが存在しなかったため、実質的にはフルバックアップとなる。そしてスナップショットファイル test.snar/home/tmp/ の概況が記録される。ここで、ファイルをひとつ新たに作成し、 2.txt は削除したとする。この時点での実像は:

/home/tmp/
         1.txt
         3.txt

ここで第二世代のバックアップを取る。スナップショットファイルは前回と同じものを指定しなければ意味がない:

tar -g /var/test.snar -C / -cf /var/test_1.tar  home/tmp

そうすると、 test_1.tar には、

/home/tmp/
         3.txt

だけがアーカイブされる。そして test.snar は、/home/tmp/ ディレクトリの現在の概況にアップデートされ、人間的に噛み砕いて言えば「1.txt は無くなった」ということが記録される。(本当は、スナップショットに書かれている情報はそうした「推移」でなくあくまでも「状態」。) ここでファイルシステムにさらなる変更を加えてみよう。 1.txt を編集して上書きしたとする。その時の状態は:

/home/tmp/
         1.txt  <-updated
         3.txt

ここで 3回目のバックアップを取る。スナップショットファイルはまた同じもの:

tar -g /var/test.snar -C / -cf /var/test_2.tar  home/tmp

test.snar スナップショットファイルは、もちろんまた現在の状況にアップデートされる。 test_2.tar には何がアーカイブされたかというと:

/home/tmp/
         1.txt

だけだ。

もうお分かりだと思うが、インクリメンタルバックアップしたアーカイブを使ってリストアする際には、第一世代のバックアップから始めて、順番に全ての増分アーカイブを展開して行かなくてはいけない。こんな具合だ:

tar -g /var/test.snar -C / -xf /var/test_0.tar
tar -g /var/test.snar -C / -xf /var/test_1.tar
tar -g /var/test.snar -C / -xf /var/test_2.tar

もしも、インクリメンタルバックアップ時に前回のアーカイブを上書きしたり、インクリメンタルリストアの時に中途のアーカイブをすっ飛ばしたらどうなるか。やってみよう:

tar -g /var/test.snar -C / -xf /var/test_0.tar
tar -g /var/test.snar -C / -xf /var/test_2.tar

リストアされたフォルダはこうなってしまう:

/home/tmp/
         1.txt

test_0.tar によって 1.txt, 2.txt が一度はリストアされるが、test_2.tar アーカイブをインクリメンタルリストアした段階で、1.txt は新しいものに置き換えられ、2.txt は削除される。しかし test_2.tar は 3.txt の実体そのものを格納していないので、結果、 3.txt はファイルシステム上から消失したままになってしまうのだ。

ついでに、スナップショット情報の性格を試すために、もうひとつ実験してみよう。この状態で、4.txt というファイルを新たに作成してみる。そこでさっきの test_2.tar をインクリメンタルリストアすると、1.txt は再度上書きされ、4.txt は削除されるのだ。

インクリメンタルバックアップのまとめ

なお、「もしや」と思って、第二世代インクリメンタルバックアップ時に -cf-rf つまり、新しいアーカイブを作成する代わりに既存の第二世代アーカイブに増分だけを追加 (append) できないかとやってみたが、 tar はそういう動作を受け付けなかった。

マルチボリュームバックアップ

-M (--multi-volume) オプションを指定すれば、複数のメディアやファイルに分割してアーカイブできる。メディア直書き出し (例えばテープ) の場合はメディアの終端に達すれば tar が次のメディアの挿入や次の書き出し先の指定を促してくるが、書き出し先がファイルの場合には、必ず --tape-length=<num> オプション (<num> の単位はKB ) も指定してやる必要がある。そうすれば、メディア直書き出しの時と同じように tar が次のファイル名を尋ねてくるようになる。ただし、GNU tar は圧縮とマルチボリュームを同時に扱うことができないので、マルチボリューム動作を指定するなら圧縮をあきらめなくてはならない。

cron などで自動化する際には、いちいちファイル名の受け答えをするわけにはいかない。それを避けるには、 -f オプションでファイル名を複数渡しておく。渡したファイル名よりも実際の分割数が多くなると、次のファイル名を訊いてくるが、これは、 -f オプションを「多め」に渡しておくことで解決できる。多く渡しすぎても特に問題は起こらず、ただ余ったファイル名は使われないだけ。例えば個々のアーカイブが 700M の CD に納まるようにしたい場合、コマンドは次のような塩梅になる:

tar -c -M --tape-length=716000 \
  -f arch_01.tar -f arch_02.tar -f arch_03.tar  some/dir another/dir 
マルチボリュームアーカイブのリストア

やり方はふた通りある。まず、バックアップの際と同様に -f を複数指定する方法:

tar -xv -M -f arch_01.tar -f arch_02.tar -f arch_03.tar

もうひとつは、最初のアーカイブひとつだけを指定して tar に催促させる方法:

tar -xv -M -f arch_01.tar

こうすると tar が `Prepare ... return:' というプロンプトの状態でポーズするので、次のアーカイブファイル名をタイプ。さらにもう一度プロンプトが出たら y をタイプ:

Prepare volume #2 for `arch_01.tar' and hit return: n arch_02.tar
Prepare volume #2 for `arch_02.tar' and hit return: y

プロンプトで使えるコマンド一覧