カテゴリー
【シェルコマンド基礎講座】rsyncコマンドの使い方を細かく検証しながらinclude/excludeのコツを覚える
※ 当ページには【広告/PR】を含む場合があります。
2023/09/19
目次
- 1. rsyncコマンドを使いこなすメリットとは?
- 2. rsyncコマンドを具体例から小出しに考えてみる
- 2-1. rsyncコマンドの基本的な使い方
- 2-2. include/excludeオプションで同期対象をフィルタリングする
- 2-3. rsyncのフィルタの基本は除外(exclude)していく
- 2-4. ファイル名に「*」を付ける・付けないの違い
- 2-5. rsyncで使えるフィルタの簡易正規表現 〜 『*』・『**』・『?』
- 2-6. 注意が必要なrsyncのフィルタで「/(スラッシュ)」のルール
- 2-7. 特定のフォルダを除外する
- 2-8. 特定の階層の特定のファイルだけ除外する
- 2-9. 同一ファイル名の指定があった場合には先に指定されたほうが優先
- 2-10. ルート階層のファイルだけ含める・除外する
- 2-11. 複数のファイルパターンでマッチさせる
- 3. まとめ
この記事ではrsyncコマンドを使いこなす上で重要な以下のポイントを中心に、具体的な利用例をもって説明していこうと思います。
+ rsync独自の簡易正規表現を良く理解しよう
+ 基本的にはexcludeでフィルタリングしていこう
+ フィルタを付ける順序には気をつけよう
rsyncコマンドを使いこなすメリットとは?
個人的なユースケースをあげると、
1. SSH経由でネットワーク上にある別のローカルマシーン上のリソースを同期
2. AWS EC2などのクラウド上のLinuxインスタンスにあるリソースを同期
3. Docker等でホストOSとコンテナ間のリソースを同期
などなど、色々応用が考えられます。
Linuxの標準コマンドではありませんが、オンラインで何かしらのプロジェクトを開発する際には不可欠とも言えます。
仮にrsyncを使わないとなると、2つのフォルダ間で同期するスクリプトを自作するのは大変ですが、おおよそ
find
grep
read
SRC_DIR=./src
DST_DIR=./dst
find ${SRC_DIR} -maxdepth 1 | grep -E "<...特定のファイル名でフィルタ>" | while read -r fname; do
cp "$fname" "${DST_DIR}/"
done
これだとコピーさせたいファイルを見つけて、単純にファイルを保存先にコピーするだけですので、rsyncコマンドと比べるべくもなく、おおよそ"同期"とは呼べない代物ものですが、ここから頑張って果てしないカスタマイズしていけば...いつかはrsyncに辿り着けるかもしれません。
そんなスクリプトの実装に苦労をするよりも、「rsync」コマンドの使い方をしっかり覚えておけば、さまざまな開発シーンで役に立つので、ここで頑張って勉強しておきましょう。
rsyncコマンドを具体例から小出しに考えてみる
Debian系・Ubuntu系などaptが使える場合には、以下のコマンドで一発導入することができます。
$ sudo apt install rsync
$ rsync --version
rsync version 3.2.3 protocol version 31
Copyright (C) 1996-2020 by Andrew Tridgell, Wayne Davison, and others.
Web site: https://rsync.samba.org/
#...
他のLinuxディストリビューションの殆どのパッケージマネージャからでも同様の操作で導入することができると思います。
rsyncコマンドの基本的な使い方
同期元のフォルダを
src
dst
src
$ rsync -av src/ dst/
#もしくは
$ rsync -avP src/ dst/
コマンドオプションに付いている
-av/-avP
rm -rf
オプションに関して気になることがあれば、
「rsync --help」
ここで実際に例をみながらいくつか実験してみましょう。
とりあえず実験用に同期元の
src
fuga
moga
$ tree -a src
src
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ ├── fugafuga.txt
│ └── piyo.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
ここで、ファイル・フォルダのフィルタリングは一旦考えず、
src
dst
$ rsync -av src/ dst/
$ tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ ├── fugafuga.txt
│ └── piyo.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
当然ながら、丸ごとリソースがもれなくコピーされています。
include/excludeオプションで同期対象をフィルタリングする
rsyncコマンドの真骨頂はなんといっても
rsyncコマンドの
include/exculde
まずincludeから理解してみましょう。
さきほど最初に紹介していた一見なんのフィルタも持たないコマンド例ですが、実際には
すべてのフォルダ・ファイルを含む
$ rsync -av src/ dst/
#👇明示な書き換え
$ rsync -av --include="*" src/ dst/
理解しておきたいのが、
「*」(アスタリスク文字)
すべてのファイル名にマッチ
なので、
--include="*"
これを理解していると、初心者でやりがちなたとえばここでの例でいうと「fuga」フォルダの中身だけ同期させたい、と意図して以下のようにやってしまっても、
$ rsync -av --include="fuga/" src/ dst/
$ tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ ├── fugafuga.txt
│ └── piyo.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
結果は
fuga
src
これは、よくよく考えれば当たり前で、
$ rsync -av --include="fuga/" src/ dst/
#👇明示に書き換え
$ rsync -av --include="*" --include="fuga/" src/ dst/
であり、もともとすべてファイルを含める(
「"*"」
fuga
rsyncのフィルタの基本は除外(exclude)していく
では同期させたいフォルダ・ファイルをどのようにフィルタリングするとうまくいくのかを考えてみましょう。
ここで出てくるのが、もう一つの
exclude
このオプションで指定したファイルやフォルダは同期から除外し、含めなくすることができます。
たとえば、特定のファイルをexclude指定したとしましょう。
$ rsync -av --exclude="piyo.txt" src/ dst/
$ tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ └── fugafuga.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
└── moga
├── a.moga.txt
├── b-moga.txt
├── c_moga.txt
├── moga.log
├── moga.txt
└── mogamoga.txt
ここでは、
piyo.txt
注目すべきはルートディレクトリだけでなく、
fuga
moge
piyo.txt
同じ結果になるという意味では、
$ rsync -av --exclude="piyo.txt" src/ dst/
#👇これでも同じ結果になる
$ rsync -av --exclude="*piyo.txt" src/ dst/
ともできます。
「*piyo.txt」
ルート
fuga
moga
piyo.txt
rsyncコマンドでは、独自の
「*」
この「簡略式正規表現」に関して、次の節から詳しく説明していきましょう。
ファイル名に「*」を付ける・付けないの違い
さきほどはさらりと説明を流しましたが、rsyncのフィルタ規則で最も目にする
まずは、
「*」
$ rsync -av --exclude="hoge.txt" src/ dst/
$tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ ├── fugafuga.txt
│ └── piyo.txt
├── hoge.log
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
となって、
src
hoge.txt
次にこのフィルタを、
「*hoge.txt」
$ rsync -av --exclude="*hoge.txt" src/ dst/
$ tree -a dst
dst
├── .hoge
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ ├── fugafuga.txt
│ └── piyo.txt
├── hoge.log
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
すると、今度は同期結果が異なって、
hoge.txt
hoge.txt
a.hoge.txt
b-hoge.txt
c_hoge.txt
hogehoge.txt
ということで、rsyncでのフィルタ中のマッチ文字
「*」
これは一般の正規表現で表現した場合、
『*』-->『[^/]+』
つまりは正規表現で書き換えると、
『*hoge.txt』-->『[^/]+hoge\.txt$』
rsyncで使えるフィルタの簡易正規表現 〜 『*』・『**』・『?』
先ほど
『*』
`『
との違いは、「/」文字をマッチに含めるか含めないかの違いだけですので、先ほどの例を
と変えてあげても結果に違いはありません。
使い所としてはフォルダ構造が深くネストしてくると、
fuga/a/hoge.txt
fuga/b/c.hoge.txt
fuga/d/e/f.hoge.txt
fuga/d/g/h.i.hoge.txt
...
などのように、下位のフォルダ構造のファイルを根こそぎマッチさせることが出来る利点があります。
また
『?』
意味合いで言うと、
『?』-->『[^/]』
あまり使いどころはないかもしれませんが、例えば、
x.hoge.txt
x-hoge.txt
x_hoge.txt
xxhoge.txt
...
と言うファイル名にマッチさせることができます。
注意が必要なrsyncのフィルタで「/(スラッシュ)」のルール
rsyncコマンドのフィルタ表現において「/(スラッシュ)」文字を扱う上で、覚えておかないと困惑するであろうルールがあるので、ここで少し説明しておきます。
まず、
これは一般の正規表現でいう、文字列の先頭を示す
『^』
例えば、
hoge.txt(ルートフォルダ内のみ)
つまり、等価な正規表現で言うと
『/hoge.txt』-->『^hoge\.txt$』
ファイルパスの先頭に厳密マッチするので、たとえば下位のフォルダに存在する
hoge.txt
fuga/hoge.txt
また、
moga(フォルダ)
a/moga(フォルダ)
a/b/moga(フォルダ)
...
この例では、
「moga」と言うフォルダ
「moga」と言うファイル
特定のフォルダを除外する
ここからまたいくつかの使用例の紹介に戻りましょう。
先ほどの内容でも紹介したように、特定のフォルダを除外したい場合は、以下のように書けます。
$ rsync -av --exclude="fuga/" src/ dst/
$ tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
パターンに
<フォルダ名>/
「/」(スラッシュ)
特定の階層の特定のファイルだけ除外する
まずは、ピンポイントに除外したいファイルパスを指定するやり方をやってみます。
$ rsync -av --exclude="fuga/piyo.txt" src/ dst/
$ tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ └── fugafuga.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
狙ったファイルだけ弾かれています。
もしくは逆パターンで、指定したファイル以外は残すようなやり方も試してみましょう。
$ rsync -av --include="fuga/piyo.txt" --exclude="fuga/*" src/ dst/
#👇でも同じ
#rsync -av --include="piyo.txt" --exclude="fuga/*" src/ dst/
#rsync -av --include="*piyo.txt" --exclude="fuga/*" src/ dst/
$ tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ └── piyo.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
ただし、
「*」
--exclude="fuga/"
$ rsync -av --include="piyo.txt" --exclude="fuga/" src/ dst/
$ tree -a dst
dst
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
同一ファイル名の指定があった場合には先に指定されたほうが優先
あまり意味が無い比較かもしれませんが、なんらかの手違いで同一のファイルでinclude/exclude指定が重複していたパターンを考えます。
まずは
exclude
include
$ rsync -av --exclude="piyo.txt" --include="piyo.txt" src/ dst/
$ tree -a dst/
dst/
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ └── fugafuga.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
└── moga
├── a.moga.txt
├── b-moga.txt
├── c_moga.txt
├── moga.log
├── moga.txt
└── mogamoga.txt
こちらは除外が優先され、
piyo.txt
これを逆にするとどうでしょう。
$ rsync -av --include="piyo.txt" --exclude="piyo.txt" src/ dst/
$ tree -a dst/
dst/
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ ├── fugafuga.txt
│ └── piyo.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ ├── mogamoga.txt
│ └── piyo.txt
└── piyo.txt
この場合には、includeが優先され、除外されない、という結果になります。
つまりファイルもしくはフォルダのマッチパターンには、
ルート階層のファイルだけ含める・除外する
ルート階層のファイルは、そのファイルパスに
「<ファイル名>」(「/」文字を含まない)
ルート階層以外の
piyo.txt
piyo.txt
$ rsync -av --exclude="*/piyo.txt" src/ dst/
$ tree -a dst/
dst/
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ └── fugafuga.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
├── moga
│ ├── a.moga.txt
│ ├── b-moga.txt
│ ├── c_moga.txt
│ ├── moga.log
│ ├── moga.txt
│ └── mogamoga.txt
└── piyo.txt
とすれば良いでしょう。
この場合、
/fuga/piyo.txt
/moga/piyo.txt
/piyo.txt
逆に、ルート階層の
piyo.txt
$ rsync -av --exclude="/piyo.txt" src/ dst/
$ tree -a dst/
dst/
├── .hoge
├── a.hoge.txt
├── b-hoge.txt
├── c_hoge.txt
├── fuga
│ ├── a.fuga.txt
│ ├── b-fuga.txt
│ ├── c_fuga.txt
│ ├── fuga.log
│ ├── fuga.txt
│ ├── fugafuga.txt
│ └── piyo.txt
├── hoge.log
├── hoge.txt
├── hogehoge.txt
└── moga
├── a.moga.txt
├── b-moga.txt
├── c_moga.txt
├── moga.log
├── moga.txt
├── mogamoga.txt
└── piyo.txt
こちらは、ルート階層の
/piyo.txt
複数のファイルパターンでマッチさせる
ここまででおそらくrsyncのinclude/excludeのおおよその使い方を理解してもらったのではなかろうかと思います。
では、最後にフィルタパターンをリレーして少々複雑なファイル同期をやってみましょう。
$ rsync -av \
--exclude=".*" \
--exclude="hoge*.txt" \
--exclude="*fuga.*" \
--exclude="??g?.log" \
--exclude="a.*" \
--exclude="b-*.*" \
--exclude="/piyo.txt" \
--exclude="/**moga.txt" \
src/ dst/
$ tree -a dst/
dst/
├── c_hoge.txt
├── fuga
│ └── piyo.txt
└── moga
└── piyo.txt
どのパターンがどのファイルパスにマッチしているのかは、詳しい解説は省略しますが、上記までの内容を順番に追っていただくと、なんとなく読めると思います。
まとめ
以上、rsyncの利用法の実例からinclude/excludeのフィルタの考え方をじっくりと解説してきました。
rsyncコマンドを使いこなす上で、重要な項目を箇条書きでまとめると、
+ rsync独自の簡易正規表現を良く理解しよう
+ 基本的にはexcludeでフィルタリングしていこう
+ フィルタを付ける順序には気をつけよう
ということを中心に説明してみました。
では、良いLinuxライフを。 めでたしめでたし。
参考サイト
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー
- 1. rsyncコマンドを使いこなすメリットとは?
- 2. rsyncコマンドを具体例から小出しに考えてみる
- 2-1. rsyncコマンドの基本的な使い方
- 2-2. include/excludeオプションで同期対象をフィルタリングする
- 2-3. rsyncのフィルタの基本は除外(exclude)していく
- 2-4. ファイル名に「*」を付ける・付けないの違い
- 2-5. rsyncで使えるフィルタの簡易正規表現 〜 『*』・『**』・『?』
- 2-6. 注意が必要なrsyncのフィルタで「/(スラッシュ)」のルール
- 2-7. 特定のフォルダを除外する
- 2-8. 特定の階層の特定のファイルだけ除外する
- 2-9. 同一ファイル名の指定があった場合には先に指定されたほうが優先
- 2-10. ルート階層のファイルだけ含める・除外する
- 2-11. 複数のファイルパターンでマッチさせる
- 3. まとめ