Copy-itemを使ったディレクトリコピーはしないほうが無難なのでは、という話

Copy-itemを使ったディレクトリコピーはコピー先に指定するディレクトリの有無に依存して挙動が変わってしまう。

面倒でもディレクトリコピーはしないほうが無難であり、コピー先フォルダを作成した上で、コピー元フォルダ以下をワイルドカードコピーすべきでは、という話

というか、ディレクトリコピーをするならPowershellにこだわらずrobocopyを使ったほうが良いのだろうと思う*1

環境

PS D:\SandBox> "$((get-wmiobject win32_operatingsystem).caption) ($((get-wmiobject win32_operatingsystem).version))"
Microsoft Windows 10 Pro (10.0.10586)

PS D:\SandBox> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      0      10586  494 

実際に試してみる

PS D:\SandBox> #フォルダ構成

PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
└─from
        test1.txt
        test2.txt
        

PS D:\SandBox> #コピー先ディレクトリが存在しない場合にディレクトリコピーする

PS D:\SandBox> Copy-Item .\from .\to -Recurse -Force -PassThru


    ディレクトリ: D:\SandBox


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:35                to                                                                                                                            


    ディレクトリ: D:\SandBox\to


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
-a----       2016/09/19     15:31             14 test1.txt                                                                                                                     
-a----       2016/09/19     15:31             14 test2.txt                                                                                                                     



PS D:\SandBox> #コピー先フォルダが作成され、ディレクトリがコピーされる

PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
├─from
│      test1.txt
│      test2.txt
│      
└─to
        test1.txt
        test2.txt
        

PS D:\SandBox> 
PS D:\SandBox> #上記のようにコピー先フォルダが存在する状態で、先ほどと同じコマンドでディレクトリコピーする

PS D:\SandBox> Copy-Item .\from .\to -Recurse -Force -PassThru


    ディレクトリ: D:\SandBox\to


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:37                from                                                                                                                          


    ディレクトリ: D:\SandBox\to\from


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
-a----       2016/09/19     15:31             14 test1.txt                                                                                                                     
-a----       2016/09/19     15:31             14 test2.txt                                                                                                                     



PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
├─from
│      test1.txt
│      test2.txt
│      
└─to
    │  test1.txt
    │  test2.txt
    │  
    └─from
            test1.txt
            test2.txt
            

PS D:\SandBox> #コピー先ディレクトリの配下に、コピー元フォルダが複製される

回避策

コピー先の有無によって挙動が変わるのは再現性がないため避けたい。 以下のように、ディレクトリコピーではなく、ディレクトリ配下をワイルドカードでコピーするようにすると、フォルダが存在しない場合はエラーとなる。

PS D:\SandBox> Copy-Item .\from\* .\to\ -Recurse -Force -PassThru
Copy-Item : ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。
発生場所 行:1 文字:1
+ Copy-Item .\from\* .\to\ -Recurse -Force -PassThru
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Copy-Item], IOException
    + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Commands.CopyItemCommand
 

PS D:\SandBox> md .\to\


    ディレクトリ: D:\SandBox


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:46                to                                                                                                                            



PS D:\SandBox> ls


    ディレクトリ: D:\SandBox


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
d-----       2016/09/19     15:31                from                                                                                                                          
d-----       2016/09/19     15:46                to                                                                                                                            



PS D:\SandBox> Copy-Item .\from\* .\to\ -Recurse -Force -PassThru


    ディレクトリ: D:\SandBox\to


Mode                LastWriteTime         Length Name                                                                                                                          
----                -------------         ------ ----                                                                                                                          
-a----       2016/09/19     15:31             14 test1.txt                                                                                                                     
-a----       2016/09/19     15:31             14 test2.txt                                                                                                                     



PS D:\SandBox> tree /f D:\SandBox
フォルダー パスの一覧
ボリューム シリアル番号は 4641-823A です
D:\SANDBOX
├─from
│      test1.txt
│      test2.txt
│      
└─to
        test1.txt
        test2.txt
        

結論

ディレクトリコピーするときはPowershellを使わないほうが良いと思う(2016/09/19時点)。 なお、このあたりの詳細な挙動については、Copy-Item -Recurse の振舞が詳しい

*1:robocopyコマンドを使ったことがないので確証はない。後日調べる。