オフィシャルドキュメント: 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の使い方

基本前の基本 - オプションのスタイルのこと

是非読んでおきたい文書: GNU tar Manual - The Three Option Styles

--preserve-permissions のようなロングオプションスタイル、-p のようなショートスタイルについては特に疑問の余地はない。もうひとつの指定形式である、`-' も `--' も付けない `tar xzvf ...' のような形式が少し曲者だ。筆者は長い間、最後のスタイルが新しいのだと思っていた。ところが実は逆で、こっちのほうがオールドスタイルなのだという。Linux を触り始めた時期や、初期に取り組んだソフトウェアが保守中の保守 qmail であったため、自分としてはダッシュなしスタイルが一番普通で専らこれを使う習慣がついていた。もちろん、適所でロングオプションやショートオプションをミックスして使っている。オールドスタイルには、以下の規則性がある。

tar xzfCv archive.tar.gz /tmp file/to/extact

`zxfCv' の中で引数をとるオプションは fC なので、archive.tar.gzf オプションの引数、/tmpC オプションの引数として解釈される。ただし、こんなコマンド例はあまり目にしたことがない。実際は、

tar xzvf archive.tar.gz -C /tmp file/to/extact

とやるのが普通だろう。ただ、それでもオールドスタイルがオプションと引数を同じ順序で読み取るという原則は変わらないので、

tar xzfv archive.tar.gz -C /tmp file/to/extact

は成り立つ一方、ショートオプションで

tar -xzfv archive.tar.gz -C /tmp file/to/extact

は意図通りにいかない。なぜなら、ショート -f は直後に書庫ファイル引数を要求するので、archive.tar.gz でなく「v という書庫ファイルを解凍せよ」と受け取るからだ。

基本

当ページでは、書庫作成時に -C オプション併用する例をたくさん書いているが、後述する除外オプション (--exclude--exclude-from) がうまく機能しないことが分かった。特に、除外指定にワイルドカードを含む場合だ。`-C /' オプションを使う代わりに、きっぱりと事前に `cd /' しておく方が変な挙動に悩まされずに済む。

/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 gzip 圧縮/解凍を行う
-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 が書庫に保存される。書庫内には、それぞれ -C による移動先からの相対パス (つまりこの場合はパスなしの file1file2) としてファイルが格納される。
-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 に左右される。tarコマンドの実行者が root の場合のみデフォルト。
-O, --to-stdout ファイルを実体として解凍する代わりに、そのファイルの内容を stdout に出力。内容を確認したり、他のプログラムにパイプ渡しする時に便利。
-k,
--keep-old-files
ファイルを上書きしない。ファイルが既に存在するとエラー扱いとなる。
--skip-old-files `-k' と同じだが、エラー扱いとしない。
--keep-newer-files tar 1.15(RHEL/CentOS5) にはない。書庫内のファイルの方が新しい場合のみ上書きする。タイムスタンプが同じ場合もスキップされる。正常扱い。
--overwrite ファイルを上書きする (解凍時はデフォルト)。
--overwrite-dir ディレクトリの属性を上書きする (解凍時はデフォルト)。
--no-overwtite-dir 前記の否定。tar 1.15(RHEL/CentOS5) にはない。
--wildcards 解凍するファイルやディレクトリ名を決め打ちで解凍する場合に、ワイルドカードの使用を可能にする (デフォルト)。tar 1.15(RHEL/CentOS5) では、当オプションは書庫作成時の --exclude に対する作用しかなく、解凍時に指定しても無視される。
--no-wildcards 前記の否定。tar 1.15(RHEL/CentOS5) では、同様に、書庫作成時の --exclude に対する作用しかなく、つまり既定で有効であるワイルドカード機能を無効にすることができない。
--no-anchored tarの書庫内にはオブジェクトはパス付きで保存されているので、通常、myfile.txthome/hoge/myfile.txt./myfile.txt にはヒットしない。このオプションを指定すると、ヒットするようになる。tar 1.15(RHEL/CentOS5) では、上記同様、解凍には作用してくれない。

というわけで、バックアップという用途で、また 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 オプションを「多め」に渡しておくことで解決できる。多く渡しすぎても特に問題は起こらず、ただ余ったファイル名は使われないだけ。例えば個々のアーカイブが 700MB の 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

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

解凍時のコツ

ソフトウェアのインストールなどで普段解凍する時は、単純に、

tar xzvf source.tar.gz 

あるいは、

tar xzpvf source.tar.gz

のように使うことが多いだろう。tar は、rootで使う時だけデフォルトで -p (--preserve-permissions) が効いている。一般ユーザで使う時は umask の影響を受けてしまうので、明示的に -p オプションを使ったほうがよい時がある。もちろん、一般ユーザがファイルやディレクトリに他ユーザの権限を付与することはできないので、書庫内に収まった hoge:root なファイルをその通り解凍するのは無理な相談で、この場合、-p を指定したとしても hoge:hoge として解凍される。

書庫内から特定のファイルやディレクトリだけを取り出したい時は、追加の引数としてそれらを指定する。ファイルやディレクトリは、書庫に格納されている通りの名前で指定しなければならない。書庫には、たいていの場合オブジェクトはパス付きで格納されていることを意識しておかないと、"Not found in archive" というエラーを受け取ることになる。よって、格納されているパス情報が定かでない時は、事前に例えば、

tar tzf source.tar.gz |fgrep main.c

とやって格納されている正確なオブジェクト名を調べておいてから、

tar xzvf source.tar.gz ./source/code/main.c

のように解凍する。格納名が不確かなままでも手はある。解凍アクションの際にはデフォルトでワイルドカードが有効になっているので、

tar xzvf source.tar.gz */main.c

あるいは、RHEL/CentOS 6 の tar 1.23 なら (RHEL/CentOS 5 の tar 1.15 では駄目)、--no-anchored オプションという手もある。

tar xzvf source.tar.gz --no-anchored main.c

ただし、どちらにしろ、解凍時のファイル/ディレクトリ指定は `/' で区切られた文字列単位にしかヒットしないので、./souce/code/main.c.diff というオブジェクトは、上記のどちらでもヒットしない。敢えてやるなら、

tar xzvf source.tar.gz --no-anchored main.c.*
tar xzvf source.tar.gz */main.c.*

ならヒットする。

バックアップ目的で設定ファイル類を書庫化している場合、元の場所とは異なる場所へとりあえず一旦取り出したいというケースもあるだろう。そういう時は、仮解凍先ディレクトリへ cd してから解凍してもよいし、-C を使い、

tar xzvf backup.tar.gz -C /temp/dir [file/to/extract] [another/to/extract ...]

とやる手もある。いずれにしろ、オブジェクトは必ずパスを伴って解凍される。file/to/extract を取り出すよう指定したのなら /temp/dir/file/to/extract に取り出される。-C オプションの使用上の注意は、解凍するディレクトリやファイルを指定する場合は `-C DESTINATION ' を必ずその直前で指定すること。というのも、-C オプションは複数回指定することも可能で、(滅多にやらないが)

tar xzvf backup.tgz -C /temp/dir etc/hosts -C /another/dir etc/sysconfig/network

とやると、etc/hosts/temp/dir/ 配下へ、etc/sysconfig/network/another/dir/ 配下へ解凍されるのだ。取り出すオブジェクトを指定しない時は、-C オプションはコマンドの最後に置くのが穏当だ。

-k (--keep-old-files) と --no-overwrite-dir を同時指定すると、なぜだか -k は無視されてしまう。