すごく基本的なことだと思うのですが
自分は知らなかった cp -r
の動作についてです。
基本中の基本のディレクトリの再帰的コピー
あるディレクトリとその中にファイルを入れたとします:
mkdir /tmp/dir1 && touch /tmp/dir1/some.txt
ここでディレクトリ dir1
内には some.txt
が生成されました。
次に、この dir1
を dir2
としていつものようにコピーします:
cp -r /tmp/dir1 /tmp/dir2 && ls -la /tmp/dir*
結果は予想通り、同じ構造をもったディレクトリ dir1
, dir2
ができました:
/tmp/dir1:
total 0
drwxr-xr-x 3 user wheel 96 Feb 15 11:58 .
drwxrwxrwt 19 root wheel 608 Feb 15 11:58 ..
-rw-r--r-- 1 user wheel 0 Feb 15 11:58 some.txt
/tmp/dir2:
total 0
drwxr-xr-x 3 user wheel 96 Feb 15 11:58 .
drwxrwxrwt 19 root wheel 608 Feb 15 11:58 ..
-rw-r--r-- 1 user wheel 0 Feb 15 11:58 some.txt
2 回目のコピー
ここで、更に先ほどのコピーのコマンドを実行します:
cp -r /tmp/dir1 /tmp/dir2 && ls -la /tmp/dir*
この結果は先程の結果とは異なり、
dir2
内に dir1
が子としてコピーされています:
/tmp/dir1:
total 0
drwxr-xr-x 3 user wheel 96 Feb 15 11:58 .
drwxrwxrwt 19 root wheel 608 Feb 15 11:58 ..
-rw-r--r-- 1 user wheel 0 Feb 15 11:58 some.txt
/tmp/dir2:
total 0
drwxr-xr-x 4 user wheel 128 Feb 15 11:58 .
drwxrwxrwt 19 root wheel 608 Feb 15 11:58 ..
drwxr-xr-x 3 user wheel 96 Feb 15 11:58 dir1
-rw-r--r-- 1 user wheel 0 Feb 15 11:58 some.txt
この動作の違いは
cp -r
では最後に指定されたコピー先がディレクトリの場合、
指定したファイルをディレクトリ内に作成するようになっているためです。
そのため、コピー先が存在する場合は cp -r
の動作は
コピー先が存在しない初回とは違う結果になるというわけです。
よくあるバッチをさっくり書いて定常的に動かしていると
いつのまにか意図しない結果になっているしれないので注意が必要ですね。
私は恥ずかしながらこの動作を知らず
冪等性のある操作だと思っていました……。
ずっと使ってきたのに。
いつも同じ動作にしたいときは?
-T
(--no-target-directory
) オプションを追加します。
このオプションを追加すると、
先程のようにコピー先のディレクトリが存在する場合でも
そのままディレクトリごとコピーされます:
cp -rT /tmp/dir1 /tmp/dir2 && ls -la /tmp/dir*
※
ただし、このオプションは GNU 版の cp にしかないと思われて、
Mac や BSD の子孫の cp ではディレクトリを移動して差し替えか、
単に削除しておくということが必要そうです。
(実はいいやりかたがあれば教えてください)
以上、無知の知でした。