带有黑名单(排除)和白名单(包括)的 Powershell 复制文件 [英] Powershell Copy Files with a Blacklist (Exclude) and a Whitelist (Include)

查看:50
本文介绍了带有黑名单(排除)和白名单(包括)的 Powershell 复制文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将一些 msbuild 脚本翻译成 powershell.

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

如下图所示:

'$(ArtifactDestinationFolder)\%(RecursiveDir)%(Filename)%(Extension)')"/></目标></项目>

我可以在 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')复制项目 $sourceDirectory $destinationDirectory -Recurse -Include $includeFiles -Exclude $excludeFiles# -Container:$false

附加:

我试过了:

$sourceDirectory = 'c:\windows\System32'$destinationDirectory = 'C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults'$excludeFiles = @('EventViewer_EventDetails.xsl')$includeFiles = @('*.xsl','*.xslt','*.png','*.jpg')复制项目 $sourceDirectory $destinationDirectory -Recurse -Include $includeFiles -Exclude $excludeFiles

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

我试过这个:

$sourceDirectory = 'c:\windows\System32'$destinationDirectory = 'C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults'$excludeFiles = @('EventViewer_EventDetails.xsl')$includeFiles = @('*.xsl','*.xslt','*.png','*.jpg')复制项目 $sourceDirectory $destinationDirectory -Recurse -Include $includeFiles -Exclude $excludeFiles -Container:$false

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

解决方案

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 也不能正常工作,尽管问题更可能出现在-包括;请参阅此 GitHub 问题.

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

<小时>

案例(a) - 作为源的单个目录路径

如果源是单个目录(或者是通配符模式解析到的项目中的唯一目录),Copy-Item 也隐式地将 destination 解释为一个目录.

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

PowerShell GitHub 存储库中有一个 问题 - 理所当然- 抱怨这种违反直觉的行为.

有两种基本的解决方法:

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

# 删除一个预先存在的目标目录:如果(测试路径 $destinationDirectory){删除项目 $destinationDirectory -Recurse -WhatIf}# 现在 Copy-Item -Recurse 按预期工作.# 如前所述,-Exclude 按预期工作,但 -Include 不会.复制项目 $sourceDirectory $destinationDirectory -Recurse

警告:Remove-Item -Recurse 遗憾的是,它可能会间歇性地异步操作,甚至可能失败 - 有关可靠的替代方案,请参阅 这个答案.

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

  • 创建目标目录.一经请求;也就是说,只有当它不存在时才创建它.
  • 使用Copy-Item将源目录的内容复制到目标目录.

# 确保目标目录.存在.# -Force 是一个安静的空操作,如果它已经存在的话.$null = New-Item -Force -ItemType Directory -LiteralPath $destinationDirectory# 将源目录的 *contents* 复制到目标,使用# 通配符.# -Force 确保*隐藏的项目*,如果有的话,也包括在内.# 如前所述,-Exclude 按预期工作,但 -Include 不会.Copy-Item -Force $sourceDirectory/* $destinationDirectory -Recurse

<小时>

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

注意:

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

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

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

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

  • 如果解析项中有多个目录Copy-Item复制第一个目录,然后在第二个目录中失败并显示以下错误消息:
    容器不能复制到现有的叶项

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

I'm translating some msbuild scripts to powershell.

In msbuild, I can generate a blacklist and/or whitelist of files I want to (recursively) copy to a destination folder.

As seen below:

<?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>

Can I do the same in powershell?

I have tried the below, but it creates a file called "C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults" (its a file with no extension, not a directory)

$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

APPEND:

I tried this:

$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

(No results, not even a file with no extension)

and I tried this:

$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

(No results, not even a file with no extension)

解决方案

Copy-Item -Recurse, as of Windows PowerShell v5.1 / PowerShell Core 6.2.0, has its quirks and limitations; here's what I found:

If you have additional information or corrections, please let us know.

There are two fundamental ways to call Copy-Item -Recurse:

  • (a) specifying a directory path as the source - c:\windows\system32

  • (b) using a wildcard expression as the source that resolves to multiple items in the source directory - c:\windows\system32\*

There are two fundamental problems:

  • The copying behavior varies based on whether the target directory already exists - see below.

  • The -Include parameter does not work properly and neither does -Exclude, though problems are much more likely to arise with -Include; see this GitHub issue.

DO NOT USE THE SOLUTIONS BELOW IF YOU NEED TO USE -Include - if you do need -Include, use LotPing's helpful solution.


Case (a) - a single directory path as the source

If the source is a single directory (or is the only directory among the items that a wildcard pattern resolved to), Copy-Item implicitly also interprets the destination as a directory.

However, if the destination directory already exists, the copied items will be placed in a subdirectory named for the source directory, which in your case means: C:\work9\MsBuildExamples\FileCopyRecursive\PowershellResults\System32

There is an issue in the PowerShell GitHub repository that - rightfully - complains about this counter-intuitive behavior.

There are two basic workarounds:

If acceptable, remove the destination directory first, if it exists - which is obviously to be done with CAUTION (remove -WhatIf once you're confident that the command works as intended):

# 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

Caveat: Remove-Item -Recurse, regrettably, can intermittently act asynchronously and can even fail - for a robust alternative, see this answer.

If you want to retain a preexisting destination dir. - e.g., if you want to add to contents of the destination directory,

  • Create the target dir. on demand; that is, create it only if it doesn't already exist.
  • Use Copy-Item to copy the contents of the source directory to the target dir.

# 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


Case (b) - a wildcard expression as the source

Note:

  • If there's exactly 1 directory among the resolved items, the same rules as in case (a) apply.

  • Otherwise, the behavior is only problematic if the target item doesn't exist yet. - see below.

  • Therefore, the workaround is to ensure beforehand that the destination directory exists:
    New-Item -Force -Path $destinationDirectory -ItemType Directory

If the target item (-Destination argument) doesn't exist yet:

  • If there are multiple directories among the resolved items, Copy-Item copies the first directory, and then fails on the second with the following error message:
    Container cannot be copied onto existing leaf item

  • If the source is a single file or resolves to files only, Copy-Item implicitly interprets a non-existent destination as a file.
    With multiple files, this means that a single destination file is created, whose content is the content of the file that happened to be copied last - i.e, there is data loss.

这篇关于带有黑名单(排除)和白名单(包括)的 Powershell 复制文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆