2012年4月9日月曜日

rsync の include 使用方法(include と exclude の整理)

rsync コマンドを include オプションを指定して実行しようとしたがうまくいかなかったが、ネットをあれこれ検索していたらうまくいったのでその結果をまとめる。

その前に include と exclude オプションとは

rsync コマンドには --include オプションと --exclude オプションがある。それぞれの意味は以下の通り。
  • --include : --include="hogehoge" で指定した文字列にマッチするファイルまたはディレクトリが同期対象となる
  • --exclude : --exclude="hogehoge" で指定した文字列にマッチするファイルまたはディレクトリが同期対象から除外される
つまり /var の中の httpd.log のみを同期することや /var の中の httpd.log 以外を同期するといったことが可能となる。

じゃあやってみよう


ということで以下のようなディレクトリとファイルがあるとする。

同期対象のディレクトリ構成



これに対して以下のコマンドを実行する。
rsync -av --delete --include="config" rsync_test/ to_dir01
予想では rsync_test/config のみが to_dir01 に作成されるはずだ。だが、実際は…


となり、すべて同期されてしまう。
ここで、 man などでヘルプを見てみて--excludeと一緒に指定しないと駄目そうと判明したので以下のように実行してみた。デフォルト全て除外でconfigだけ対象という意図だ。
rsync -av --delete --exclude="*" --include="config" rsync_test/ to_dir02
これで実行した結果が以下のようになる。


きちんと指定しているにも関わらずすべて対象外となり何も同期されない。この時、
  • 指定方法がフルパスじゃないと駄目?
  • 対象がディレクトリだから最後に / がないと駄目?
という試行錯誤を小一時間繰り返したが解決しない…。

include と exclude オプションは順序が大事


man を見ても分からないのでGoogle先生に聞いてみた。そしたら以下のサイトが見つかった。


まさに今回の件にドンピシャ。ということで以下のように変更してみた。
rsync -av --delete --include="config" --exclude="*" rsync_test/ to_dir03
これで実行した結果がこちら。


うまくいった。結局は使い方が間違っていたということか。

にしても今までコマンドをいろいろ使ってきたけどコマンドラインオプションに順序性があることがあるということを初めて知った。なんとなく違和感があるけどそういうものなのか…


include と exclude の指定方法による実行結果への影響

ということでうまくいったのでいろんなパターンを試してみる。

1.includeで直下のファイル名を指定した場合

rsync -av --delete --include="Rakefile" --exclude="*" rsync_test/ p01

対象のファイルのみが同期され、それ以外のファイルディレクトリ、ファイルは除外された。

2.includeで直下のディレクトリ名を指定した場合

rsync -av --delete --include="app" --exclude="*" rsync_test/ p02

対象のディレクトリのみが同期され、そのディレクトリ内のファイル、ディレクトリ、およびそれ以外のファイル、ディレクトリは除外された。

3.includeで直下でないディレクトリ名を指定した場合(失敗)

rsync -av --delete --include="controllers" --exclude="*" rsync_test/ p03

controllersという名前のディレクトリは存在していますが、パスの指定方法は相対パス指定であるため、今回の指定方法の場合はrsync_test/controllersというディレクトリを指定していることになり、そのようなディレクトリはないため何も同期されないという結果になる。

4.includeで直下でないディレクトリ名を正しく指定した場合(失敗)

rsync -av --delete --include="app/controllers" --exclude="*" rsync_test/ p04

今回は正しく指定しているにも関わらず、対象のディレクトリが作成されていない。つまり、この指定方法ではうまくいかない。原因は後回しにし次のパターンへ。

5.includeで直下でないディレクトリ名と対象ディレクトリの親ディレクトリを同時に指定した場合

rsync -av --delete --include="app" --include="app/controllers" --exclude="*" rsync_test/ p05

今回はうまくいった。つまりサブディレクトリを指定したい場合は、対象のディレクトリに至るまでの全ての親ディレクトリを--includeで指定する必要があるということだ。

6.includeでディレクトリ名/*を指定した場合(失敗ケース)

rsync -av --delete --include="app" --include="app/controllers/*" --exclude="*" rsync_test/ p06

appまでは作成されたがapp/controllers以降が作成されなかった。これはapp/controllers/*はあくまで指定したディレクトリに存在するファイルやサブディレクトリを対象としているのであり、その時点でapp/controllersディレクトリが対象に含まれていないためにapp/controllersディレクトリが作成されないためファイルやサブディレクトリが作成されない。つまり、は次のように指定する必要がある。

7.includeでディレクトリ名/*を指定した場合

rsync -av --delete --include="app" --include="app/controllers" --include="app/controllers/*" --exclude="*" rsync_test/ p07

このように実行すれば目的の動作となる。これはなんとかならないものか…。


8.includeでディレクトリ名/**を指定した場合

rsync -av --delete --include="app" --include="app/controllers" --include="app/controllers/**" --exclude="*" rsync_test/ p08

指定したディレクトリに含まれるファイルとサブディレクトリ、そのサブディレクトリに含まれるファイルやサブディレクトリ全てが同期される。

まとめ

includeについてはひとまずこれで終了。まとめると以下のようになる。
  1. include で特定のディレクトリやファイルを指定したい場合は include を先に書いて --exclude="*" を後に指定しなければならない
  2. include でファイルやディレクトリを指定する場合は相対パスで指定しなければならない
    パターン3を参照
  3. include でサブディレクトリやサブディレクトリにあるファイルを指定する場合は、対象のファイルまたはディレクトリに至るまでのディレクトリをすべて--includeで指定しなければならない
    パターン5を参照
  4. include でディレクトリ以下のファイルを指定したい場合は対象のディレクトリ対象のディレクトリ内のファイル(ディレクトリ)は別々に --include で指定しなければならない
    → パターン7,8 を参照

余談

excludeはまとめる予定は今のところなし…。