2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PowerShell 第4回 Copy-Itemのフォルダ内ファイルのコピー動作

Posted at

はじめに

PowerShellでまたしても、はまってしまいました。
Copy-Itemの挙動が変なんです。解決はしましたが。
ということで、それを備忘としてまとめておきます。

今回実施する内容

Copy-Itemを使用して、フォルダ(c:\temp\source)に存在するファイル(テキストファイル)を別のフォルダ(c:\temp\target)にコピーします。

c:
+--Temp
   +--source
       +--test.txt
       +--test2.txt
       +--test.rtf

↓下へコピー
c:
+--Temp
   +--target

ソースコード(Git Hub)

PowerShell_04_Copy-Item

環境

OS: Windows 10 JP (64bit)
PowerShell version: 5.1.19041.1

参考

Copy-itemを使ったディレクトリコピーはしないほうが無難なのでは、という話
今回のコピー動作について、記載された記事です。解決方法の記載もあります。

Copy-Item -Recurse の振舞
今回のコピー動作について、記載された記事です。

MicrosoftのCopy-Itemのリファレンス
今回の事象について説明があります。

用語

なし

Copy-Itemの変な挙動

Copy-Itemを使用してコピー元のフォルダ内(source)に存在するファイルをコピー先のフォルダにコピーしたいのですが、コピー先のフォルダ(target)配下にコピー元のフォルダ名のフォルダが作成されて、そこにファイルがコピーされます。

c:
+--Temp
   +--source
       +--test.txt
       +--test2.txt
       +--test.rtf

↓targetの下にsourceフォルダができてそこにコピーされる。
c:
+--Temp
   +--target
      +--source
         +test.txt
         +test2.txt

ソースコード

Copy-Item_fail.ps1
try {
    $source = "C:\Temp\source"
    $target = "C:\Temp\target"
    Copy-Item -Filter "*.txt" -Path $source -Destination $target -Recurse
} catch {
    Write-Host $Error[0]
}

変な挙動の説明

単に、Copy-Itemを使用して、拡張子がtxtのファイルをコピーするだけです。
期待は、「targetフォルダにテキストファイルがコピーされる」なんですが。
少し条件があって、targetフォルダが存在する場合、この動作になります。存在しない場合は、targetフォルダが作成されてそこにファイルがコピーされ、コピーされる場所としては期待通りになります。

$targetフォルダの有無 事象
あり targetフォルダ配下にsourceフォルダが作成されてファイルコピー。
なし targetフォルダを作成しファイルコピー。

コピーを実行しようとする場合、コピー先にフォルダがあるかないかで動作が変わるのって困ります。
既存フォルダにコピーしたいかもしれないし。既存フォルダに置きたいのに、sourceフォルダができてしまうのって、やはり利便性に欠けます。

MicrosoftのReference

このことは調べてみると、きちんと記載がありました。

###Example 2: Copy directory contents to an existing directory
This example copies the contents of the C:\Logfiles directory into the existing C:\Drawings directory. The Logfiles directory isn't copied.

If the Logfiles directory contains files in subdirectories, those subdirectories are copied with their file trees intact. By default, the Container parameter is set to True, which preserves the directory structure.

Copy-Item -Path "C:\Logfiles\*" -Destination "C:\Drawings" -Recurse

注意

If you need to include the Logfiles directory in the copy, remove the * from the Path. For example:

Copy-Item -Path "C:\Logfiles" -Destination "C:\Drawings" -Recurse

###Example 3: Copy directory and contents to a new directory
This example copies the contents of the C:\Logfiles source directory and creates a new destination directory. The new destination directory, \Logs is created in C:\Drawings.

To include the source directory's name, copy to an existing destination directory as shown in Example 2. Or, name the new destination directory with the same as the source directory.

Copy-Item -Path "C:\Logfiles" -Destination "C:\Drawings\Logs" -Recurse

注意

If the Path includes *, all the directory's file contents, including the subdirectory trees, are copied to the new destination directory. For example:

Copy-Item -Path "C:\Logfiles\*" -Destination "C:\Drawings\Logs" -Recurse

Example 2は既存フォルダにコピーする場合、ディレクトリの中身をコピーする方法を記載しており、-Pathのフォルダ設定について、\*を付与すると、targetのディレクトリにそのディレクトリ配下のファイルがコピーされるとしています。
なるほど、-Pathをディレクトリとすると、ディレクトリをコピーする動作になるんですね。なので、\*をつけることで、コピー対象をディレクトリ配下のファイルに絞っていると理解しました。

Example 3は新しいフォルダにコピーする場合、-Pathはディレクトリを指定すると、targetのディレクトリを作ってその配下にファイルがコピーされるとしています。
コピーの対象はディレクトリであるものの、コピー先はフォルダがないため、targetのディレクトリの名前で生成しているということですね。
だったら、その下にさらにコピー対象のディレクトリができてもよさそうだけど、そうではないらしい。

ディレクトリをコピーしているのだからディレクトリごとコピーされる、ファイルをしたいならは\*でディレクトリ配下のファイルを対象にすればファイルコピーできるということのようです。
そういわれるとそうだなあと納得してしまいました。
-Filterで拡張子を設定していたから、ファイルが対象と思い込んでいたということです。

なお、ディレクトリ=フォルダとしています。MicrosoftのReferenceがディレクトリと記載されていたため、ここではディレクトリと記載しています。

ディレクトリ配下のファイルコピー(成功例)

では記載の通りにソースコードを直してみます。
要するに$sourceの最後に\*をつけて対象をファイルにします。
これで期待通り、ファイルだけコピーされて余分なフォルダは作成されませんでした。

ソースコード

CopyItem_success.ps1
try {
    $source = "C:\Temp\source\*"
    $target = "C:\Temp\target"
    Copy-Item -Filter "*.txt" -Path $source -Destination $target -Recurse
} catch {
    Write-Host $Error[0]
}

おわりに

すっごく簡単なことなのに、2時間ほど悩んでしまいました。なさけない。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?