web-dev-qa-db-ja.com

cpは奇妙な動作をします。 (ドット)または..(ドットドット)はソースディレクトリです。

この答え は、隠しファイルを含むすべてのファイルをディレクトリsrcからディレクトリdestに次のようにコピーできることを示しています。

mkdir dest
cp -r src/. dest

これが実際に機能する理由についての回答やコメントには説明がありません。また、これに関するドキュメントも誰も見つけていないようです。

いくつか試しました。まず、通常のケース:

$ mkdir src src/src_dir dest && touch src/src_file src/.dotfile dest/dest_file
$ cp -r src dest
$ ls -A dest
dest_file  src

次に、最後に/.を付けます。

$ mkdir src src/src_dir dest && touch src/src_file src/.dotfile dest/dest_file
$ cp -r src/. dest
$ ls -A dest
dest_file  .dotfile  src_dir  src_file

したがって、これは*と同様に動作しますが、隠しファイルもコピーします。

$ mkdir src src/src_dir dest && touch src/src_file src/.dotfile dest/dest_file
$ cp -r src/* dest
$ ls -A dest
dest_file  src_dir  src_file

.および..は、ディレクトリエントリ自体と同様に、- ここで説明 のように適切なハードリンクです。

この動作はどこから来て、どこに文書化されていますか?

18
iFreilicht

この動作は、文書化されたcp -Rのアルゴリズムの論理的な結果です。 [〜#〜] posix [〜#〜] 、ステップ2fを参照してください:

ディレクトリ内のファイルsource_fileは、ディレクトリdest_fileにコピーされます。 source_filesとしてファイルと共にここにリストされている4つのステップ(1から4)を実行します。

...は、それぞれ現在のディレクトリと親ディレクトリです。シェルに関する限り、どちらも特別なものではないため、拡張も関係なく、ディレクトリは隠しファイルを含めてコピーされます。一方、*はファイルのリストに展開され、ここに隠しファイルが除外されます。

src/.src内の現在のディレクトリで、src自体です。 src/src_dir/..src_dirの親ディレクトリであり、これもsrcです。したがって、srcの外部から、srcがディレクトリの場合、cpのソースファイルとしてsrc/.またはsrc/src_dir/..を指定することは同等であり、隠しファイルを含むsrcの内容。

src/.を指定するポイントは、srcがディレクトリ(またはディレクトリへのシンボリックリンク)でない場合は失敗しますが、srcはそうでない場合です。また、src自体はコピーせず、srcの内容のみをコピーします。これもドキュメントと一致します:

targetが存在し、既存のディレクトリに名前を付ける場合、ファイル階層内の各ファイルに対応する宛先パスの名前は、target、targetがスラッシュで終わっていない場合の単一のスラッシュ文字、およびsource_fileを含むディレクトリに対するファイルのパス名

したがって、cp -R src/. destsrcの内容をdest/.にコピーします(ソースファイルはsrc.です)。一方、cp -R src destは内容をコピーしますsrcからdest/srcへ(ソースファイルはsrcです)。

これを考えるもう1つの方法は、src/src_dirsrcを比較するのではなく、src/.src/.のコピーを比較することです。 .は、前者の場合のsrc_dirと同じように動作します。

29
Stephen Kitt

cp -R src/foo destを実行すると、dest/fooを取得します。したがって、ディレクトリdest/fooが存在しない場合は、cpによって作成され、src/fooの内容がdest/fooにコピーされます。

cp -R src/. destを実行すると、cpdest/.が存在することを確認し、src/.の内容をdest/.にコピーするだけです。

srcから.という名前のディレクトリをコピーし、その内容を既存のディレクトリdest/.とマージすると考えると、理にかなっています。

1
telcoM