--exclude
が効かない問題
現在のディレクトリ構成はこういう状態。
$ tree -N
.
├── dest
└── src
├── ä
├── b
└── c
2 directories, 3 files
src/
ディレクトリ以下のä
とb
というファイルを除外してdest/
にコピーしたいので、--exclude
で指定する。
$ rsync -av src/ --exclude ä --exclude b dest
sending incremental file list
./
ä
c
sent 156 bytes received 57 bytes 426.00 bytes/sec
total size is 0 speedup is 0.00
$ ls dest/
ä c
……なぜかä
がコピーされている。
なぜか
OS Xが採用しているHFS+では、ファイル名をNFDで正規化する。NFDでは、ä
はa
と¨
の組み合わせで表現される。
しかし、一般的にUnicodeではNFCという別の正規化手法が用いられている。NFCではä
はä
という一文字で表現される。
ターミナルで入力した文字はNFCのä
なので、NFDのa``¨
がexclude対象にならなかった。
対策
NFCをNFDに変換してからrsync
に渡してやればOK。
以下のファイルをPATHの通った場所に置いて実行権限を付ける。ファイル名はxrsync
とする。
# !/usr/bin/env ruby
require "shellwords"
system ["rsync", *ARGV].shelljoin.encode("UTF-8-MAC")
UTF-8-MAC
というのは、NFDで正規化してUTF-8で符号化した形式の通称。
実際にxrsync
を使って確認してみる。
$ xrsync -av src/ --exclude ä --exclude b dest
sending incremental file list
./
c
sent 105 bytes received 38 bytes 286.00 bytes/sec
total size is 0 speedup is 0.00
$ ls dest/
c
ä
はコピーされなくなった。
ちなみに
- Ruby 2.2.0からは
String#unicode_normalize
というメソッドでNFC/NFD正規化ができる。instance method String#unicode_normalize (Ruby 2.2.0)