使用黑名单(排除)和白名单(包括)的 Powershell 复制文件

gra*_*der 4 powershell powershell-5.0

我正在将一些 msbuild 脚本翻译为 powershell。

在 msbuild 中,我可以生成要(递归)复制到目标文件夹的文件的黑名单和/或白名单。

如下所示:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapped">

    <PropertyGroup>
        <!-- Always declare some kind of "base directory" and then work off of that in the majority of cases  -->
        <WorkingCheckout>.</WorkingCheckout>
        <WindowsSystem32Directory>c:\windows\System32</WindowsSystem32Directory>
        <ArtifactDestinationFolder>$(WorkingCheckout)\ZZZArtifacts</ArtifactDestinationFolder>
    </PropertyGroup>

    <Target Name="AllTargetsWrapped">

        <CallTarget Targets="CleanArtifactFolder" />
        <CallTarget Targets="CopyFilesToArtifactFolder" />
    </Target>

    <Target Name="CleanArtifactFolder">

        <RemoveDir Directories="$(ArtifactDestinationFolder)" Condition="Exists($(ArtifactDestinationFolder))"/>
        <MakeDir Directories="$(ArtifactDestinationFolder)" Condition="!Exists($(ArtifactDestinationFolder))"/>
        <Message Text="Cleaning done" />
    </Target>

    <Target Name="CopyFilesToArtifactFolder">

        <ItemGroup>
            <MyExcludeFiles Include="$(WindowsSystem32Directory)\**\EventViewer_EventDetails.xsl" />
        </ItemGroup>

        <ItemGroup>
            <MyIncludeFiles Include="$(WindowsSystem32Directory)\**\*.xsl" Exclude="@(MyExcludeFiles)"/>
            <MyIncludeFiles Include="$(WindowsSystem32Directory)\**\*.xslt" Exclude="@(MyExcludeFiles)"/>
            <MyIncludeFiles Include="$(WindowsSystem32Directory)\**\*.png" Exclude="@(MyExcludeFiles)"/>
            <MyIncludeFiles Include="$(WindowsSystem32Directory)\**\*.jpg" Exclude="@(MyExcludeFiles)"/>            
        </ItemGroup>        

        <Copy
                SourceFiles="@(MyIncludeFiles)"
                DestinationFiles="@(MyIncludeFiles->'$(ArtifactDestinationFolder)\%(RecursiveDir)%(Filename)%(Extension)')"
        />

        </Target>

    </Project>
Run Code Online (Sandbox Code Playgroud)

我可以在 powershell 中做同样的事情吗?

我已尝试以下操作,但它创建了一个名为“C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults”的文件(它是一个没有扩展名的文件,而不是目录)

$sourceDirectory = 'c:\windows\System32\*'
$destinationDirectory = 'C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults'
$excludeFiles = @('EventViewer_EventDetails.xsl')
$includeFiles = @('*.xsl','*.xslt','*.png','*.jpg')


Copy-Item $sourceDirectory $destinationDirectory -Recurse -Include $includeFiles -Exclude $excludeFiles
# -Container:$false
Run Code Online (Sandbox Code Playgroud)

附加:

我试过这个:

$sourceDirectory = 'c:\windows\System32'
$destinationDirectory = 'C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults'
$excludeFiles = @('EventViewer_EventDetails.xsl')
$includeFiles = @('*.xsl','*.xslt','*.png','*.jpg')


Copy-Item $sourceDirectory $destinationDirectory -Recurse -Include $includeFiles -Exclude $excludeFiles
Run Code Online (Sandbox Code Playgroud)

(没有结果,甚至没有扩展名的文件)

我尝试过这个:

$sourceDirectory = 'c:\windows\System32'
$destinationDirectory = 'C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults'
$excludeFiles = @('EventViewer_EventDetails.xsl')
$includeFiles = @('*.xsl','*.xslt','*.png','*.jpg')


Copy-Item $sourceDirectory $destinationDirectory -Recurse -Include $includeFiles -Exclude $excludeFiles -Container:$false
Run Code Online (Sandbox Code Playgroud)

(没有结果,甚至没有扩展名的文件)

mkl*_*nt0 5

Copy-Item -Recurse从 Windows PowerShell v5.1 / PowerShell Core 6.2.0 开始,有其怪癖和限制;这是我发现的:

如果您有其他信息或更正,请告知我们。

有两种基本的调用方法 Copy-Item -Recurse

  • (a) 指定目录路径作为源 -c:\windows\system32

  • (b) 使用通配符表达式作为解析为源目录多个项目的源-c:\windows\system32\*

有两个根本问题

  • 复制行为根据目标目录是否已存在而有所不同- 见下文。

  • -Include参数不能正常工作,也不能正常工作-Exclude,尽管 ; 更容易出现问题-Include。请参阅GitHub 问题 #8459

如果您需要使用,请勿使用以下解决方案-Include- 如果您确实需要-Include,请使用LotPing 的有用解决方案


情况 (a) - 作为源的单个目录路径

如果源是单个目录(或者是通配符模式解析到的项目中的唯一目录),Copy-Item则还隐式解释目标为目录。

但是,如果目标目录已存在,则复制的项目将放置在以源目录命名的子目录中,这在您的情况下意味着:C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults\System32

GitHub 问题 #2934 - 正确地 - 抱怨这种反直觉的行为

有两种基本的解决方法

如果可以接受,请首先删除目标目录(如果存在) - 显然要小心-WhatIf一旦您确信该命令按预期工作就删除):

# Remove a pre-existing destination directory:
if (Test-Path $destinationDirectory) {
  Remove-Item $destinationDirectory -Recurse -WhatIf
}

# Now Copy-Item -Recurse works as intended.
# As stated, -Exclude works as intended, but -Include does NOT.
Copy-Item $sourceDirectory $destinationDirectory -Recurse
Run Code Online (Sandbox Code Playgroud)

警告Remove-Item -Recurse遗憾的是,可能会间歇性地异步操作,甚至可能失败 - 要获得可靠的替代方案,请参阅此答案

如果您想保留预先存在的目标目录。- 例如,如果您想添加到目标目录的内容,

  • 创建目标目录。一经请求; 也就是说,仅当它尚不存在时才创建它。
  • 用于Copy-Item将源目录的内容复制到目标目录。
# Ensure that the target dir. exists.
# -Force is a quiet no-op, if it already exists.
$null = New-Item -Force -ItemType Directory -LiteralPath $destinationDirectory

# Copy the *contents* of the source directory to the target, using
# a wildcard.
# -Force ensures that *hidden items*, if any, are included too.
# As stated, -Exclude works as intended, but -Include does NOT.
Copy-Item -Force $sourceDirectory/* $destinationDirectory -Recurse
Run Code Online (Sandbox Code Playgroud)

情况 (b) - 使用通配符表达式作为源

笔记:

  • 如果已解析的项目中恰好1 个目录,则适用与情况 (a)中相同的规则。

  • 否则,只有当目标项尚不存在时,该行为才会出现问题。- 见下文。

  • 因此,解决方法事先确保目标目录存在
    New-Item -Force -Path $destinationDirectory -ItemType Directory

如果目标项(-Destination参数)尚不存在

  • 如果已解析的项目中多个目录,则Copy-Item复制第一个目录,然后在第二个目录上失败并显示以下错误消息:
    Container cannot be copied onto existing leaf item

  • 如果源是单个文件或仅解析为文件Copy-Item则隐式将不存在的目标解释为文件
    对于多个文件,这意味着创建单个目标文件内容是最后复制的文件的内容- 即,存在数据丢失