--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)