ようやく、cp でディレクトリをコピーするときのさまざまなケースをまとめた。ディレクトリも特殊なファイルの一種だが、コピー時にはやはり混同しやすい点がいくつかある。特に、コピー元パス末尾の /、ワイルドカード *、そしてコピー先ディレクトリがすでに存在するかどうかだ。
以下では [^] をスペースとして表す。/a ディレクトリの下に、ファイルまたはサブディレクトリ 1、2、3 があるものとする。
Table of Contents
cp[^]-R[^]/a/*[^]/b
次と同じ意味になる。
cp -R /a/* /b/
これは /a ディレクトリ配下で * にマッチする内容を /b ディレクトリ配下へコピーする。つまり、コピーされるのは /a の中にある各ファイルやサブディレクトリであり、/a ディレクトリそのものではない。
注意点は 2 つある。
*は shell によって展開され、デフォルトでは.gitignore、.env、.configなどの隠しファイルにはマッチしない。/aの下にサブディレクトリがある場合、それらを再帰的にコピーするには-Rまたは-rを付ける必要がある。現代の GNU coreutils では、-Rと-rは通常同等だが、移植性を重視するなら POSIX で規定されている-Rを優先して使う。
隠しファイルもまとめてコピーしたい場合は、次のようにすることを検討できる。
cp -R /a/. /b/
ここでの /a/. は、通常ファイル、隠しファイル、サブディレクトリを含めて、/a の内容を /b へコピーすることを意味する。
cp[^]-R[^]/a[^]/b/c
この書き方は、コピー先パス /b/c がすでに存在するかどうかで 2 つの場合に分けて考える必要がある。
/b/c が存在しない場合
コマンドは /b/c を作成し、/a の中にあるすべてのファイルとサブディレクトリを /b/c の中へコピーする。効果としては、/a を新しいディレクトリ /b/c としてクローンするのに相当する。
cp -R /a /b/c
コピー後の構造は次のようになる。
/b/c/1
/b/c/2
/b/c/3
/b/c がすでに存在する場合
コマンドは /a ディレクトリそのものを /b/c の中へコピーする。コピーが成功すると、ディレクトリ構造は次のようになる。
/b/c/a/1
/b/c/a/2
/b/c/a/3
つまり、コピー先ディレクトリがすでに存在する場合、cp -R /a /b/c の結果は /a の内容を /b/c に平置きするのではなく、/b/c の下にさらに a ディレクトリを作成することになる。
cp[^]-R[^]/a/[^]/b
GNU/Linux で一般的な cp 実装では、コピー元パス末尾の / は、ディレクトリをコピーするときの主な挙動を通常は変えない。つまり、
cp -R /a/ /b
は、通常は次のコマンドとほぼ同じ結果になる。
cp -R /a /b
/b がすでに存在する場合、コピー後の結果は一般に次のようになる。
/b/a/1
/b/a/2
/b/a/3
-R または -r を付けていない場合に限り、次のようなエラーが出る。
cp: omitting directory '/a/'
cp[^]-R[^]/a/[^]/b/
コピー先パス末尾の / は、コピー先がディレクトリであるべきことを示す。そのため、
cp -R /a/ /b/
は、/b がすでに存在する場合、通常 /a ディレクトリを /b の下へコピーし、結果は次のようになる。
/b/a/1
/b/a/2
/b/a/3
/b が存在しない場合、システムやバージョンによってエラーメッセージは多少異なることがある。実際に使う前に、次のコマンドで手元の環境での挙動を確認できる。
mkdir -p /tmp/cp-test/a
printf 'onen' > /tmp/cp-test/a/1
printf 'twon' > /tmp/cp-test/a/2
printf 'threen' > /tmp/cp-test/a/3
mkdir -p /tmp/cp-test/b
cp -R /tmp/cp-test/a/ /tmp/cp-test/b/
find /tmp/cp-test -maxdepth 3 -type f | sort
まとめ
よく使う結論は次のとおり。
cp -R /a/* /b/
/a の中で * にマッチした内容を /b へコピーする。デフォルトでは隠しファイルは含まれない。
cp -R /a/. /b/
隠しファイルを含め、/a の中身をすべて /b へコピーする。
cp -R /a /b/c
/b/c が存在しなければ、/a を /b/c としてクローンする。/b/c がすでに存在すれば、/a を /b/c/a としてコピーする。
cp -R /a/ /b/
一般的な GNU/Linux 環境では、コピー元パス末尾の / によって cp がディレクトリの中身だけをコピーするようになるわけでは通常ない。ディレクトリそのものではなく中身をコピーしたい場合は、cp -R /a/. /b/ と書くほうが明確である。
