Twitter見てた時、そういう処理の仕方についてわからないというのを見かけたので、一応何かの時のために残しておくことにする。 通常、LinuxやMacに入ってるmvやcpでは、コピー先のディレクトリが無かったら作成するような機能はないので、ひと手間必要になる。

1. findからxargsを使ってコピー、移動させる場合

findからxargsに対象のファイルPATHを渡して、それをcp、mvを使ってコピーや移動をさせる場合。 この場合、コピー時にディレクトリが無いとエラーになってしまうので、一度mkdirで(フォルダがない場合に)生成させる必要がある。 以下、サンプルコード(mvの場合は、cpのとこを書き換え)。

cd /path/from; find ./ -type f ... | xargs -I@ bash -c 'DIR=/path/to/dir;mkdir -p $DIR/$(dirname @);cp @ $DIR/@'

ちゃんとシェルスクリプトとかで書いて対応するなら、forやwhileを使ってやるといいだろう(xargsだと都度プロセス立ち上げるから遅いし)。

find ./ -type f ... | while read -r var;
do
  dir=/tmp/test_3;
  mkdir -p $dir/$(dirname $var);
  cp $var $dir/$var;
done

2. findからrsyncを利用してコピーさせる場合

findの結果に対し、ディレクトリの同期等を行うツールであるrsyncを使って、ディレクトリ構造とセットでコピーすることもできる。 以下、サンプルコード。

rsync -R $(find ./ -type f ...) /path/to/dir

mvと同等の処理(コピー後にオリジナルを削除する)場合は、「--remove-source-files」オプションを付与する。

rsync --remove-source-files -R $(find ./ -type f ...) /path/to/dir

パイプから渡す場合、コマンド置換してるところをcatにしとけばいいだろう。

3. findからcpの代わりにinstallコマンドを指定する

Makefileなどでよく利用されるinstallコマンドでは、コピー時にPATHがないとディレクトリを自動で作成するオプション(-D)が用意されている。 このため、cpの代わりにxargsから渡してやると、比較的カンタンにディレクトリ構造ごとコピーができる。

find ./ --type ... | xargs -I@ install -D @ /path/to/dir/@

元ファイルの削除をする機能はないので、必要であれば別途findからrmに渡してやるといいだろう。

4. findからtarによるアーカイブ化→展開を利用する場合

tarはディレクトリ構造を保持するので、それを利用する場合。 以下のように、標準入出力でtarのアーカイブ→展開をしてやればいい。

tar cf - $(find ./ -type f -name ...) | tar xf - -C /path/to/dir

こちらも、元ファイルの削除が必要な場合は別途対応が必要。