PowerShell中的排除列表Copy-Item似乎不起作用

Guy*_*Guy 64 powershell

我有以下PowerShell脚本片段:

$source = 'd:\t1\*'
$dest = 'd:\t2'
$exclude = @('*.pdb','*.config')
Copy-Item $source $dest -Recurse -Force -Exclude $exclude
Run Code Online (Sandbox Code Playgroud)

这适用于将所有文件和文件夹从t1复制到t2,但它只排除"root"/"first-level"文件夹中的排除列表,而不是子文件夹中的排除列表.

如何使其排除所有文件夹中的排除列表?

lan*_*man 92

我认为最好的方法是在Copy-Item命令中使用Get-ChildItem和pipe.

我发现这有效:

$source = 'd:\t1'
$dest = 'd:\t2'
$exclude = @('*.pdb','*.config')
Get-ChildItem $source -Recurse -Exclude $exclude | Copy-Item -Destination {Join-Path $dest $_.FullName.Substring($source.length)}
Run Code Online (Sandbox Code Playgroud)

基本上,这里发生的是你逐个浏览有效文件,然后将它们复制到新路径.最后的"Join-Path"语句是在复制文件时保留目录.该部分获取目标目录并将其与源路径之后的目录连接.

我从这里得到了这个想法,然后对它进行了一些修改以使其适用于这个例子.

我希望它有效!

  • 请注意,如果 $source 是路径对象,那么您应该编写“$source.path.length”来获取长度。 (2认同)
  • 据 MSFT 2016 年 3 月 16 日报道,该问题已得到修复,并将出现在 PowerShell 的下一个公开版本中;https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/11088483-6-years-old-bug-with-powershell-copy-item (2认同)

Whi*_*awk 9

我也有这个问题,花了20分钟在这里应用解决方案,但一直有问题.
所以我选择使用robocopy - 好吧,它不是PowerShell,但应该在powershell运行的任何地方都可用.

它开箱即用:

robocopy $source $dest /S /XF <file patterns to exclude> /XD <directory patterns to exclude>
Run Code Online (Sandbox Code Playgroud)

例如

robocopy $source $dest /S /XF *.csproj /XD obj Properties Controllers Models
Run Code Online (Sandbox Code Playgroud)

此外,它具有大量功能,如可恢复副本.文档在这里.


Shr*_*ike 6

由于评论格式代码严重,我将发布作为答案,但它只是@landyman答案的补充.建议的脚本有一个缺点 - 它将创建双嵌套文件夹.例如,对于'd:\ t1\sub1',它将创建空目录'd:\ t2\sub1\sub1'.这是因为目录的Copy-Item要求-Destination属性中的父目录名不是目录名本身.这是我找到的解决方法:

Get-ChildItem -Path $from -Recurse -Exclude $exclude | Copy-Item -Force -Destination {
  if ($_.GetType() -eq [System.IO.FileInfo]) {
    Join-Path $to $_.FullName.Substring($from.length)
  } else {
    Join-Path $to $_.Parent.FullName.Substring($from.length)
  }
}
Run Code Online (Sandbox Code Playgroud)


dan*_*und 5

exclude参数不适用于dirs.Bo的脚本的变体可以解决这个问题:

$source = 'c:\tmp\foo'
$dest = 'c:\temp\foo'
$exclude = '\.bak'
Get-ChildItem $source -Recurse  | where {$_.FullName -notmatch $exclude} | 
    Copy-Item -Destination {Join-Path $dest $_.FullName.Substring($source.length)}
Run Code Online (Sandbox Code Playgroud)


Rob*_* R. 5

请注意,语法规范要求使用 STRING ARRAY;阿拉字符串[]

SYNTAX
    Copy-Item [[-Destination] <String>] [-Confirm] [-Container] [-Credential <PSCredential>] [-Exclude <String[]>] [-Filter <String>] [-Force] [-FromSession <PSSession>] [-Include 
    <String[]>] -LiteralPath <String[]> [-PassThru] [-Recurse] [-ToSession <PSSession>] [-UseTransaction] [-WhatIf] [<CommonParameters>]
Run Code Online (Sandbox Code Playgroud)

如果您在数组生成中不明确,您最终会得到一个 Object[] - 在许多情况下会被忽略,由于类型安全而留下“错误行为”的外观。由于 PowerShell 可以处理脚本块,因此对特定于类型的变量以外的其他变量(以便可以确定有效字符串)的评估将为执行策略宽松的任何系统的注入模式攻击的潜力留下机会。

所以这是不可靠的:

PS > $omissions = @("*.iso","*.pdf","*.zip","*.msi")
PS > $omissions.GetType()

Note the result....
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
Run Code Online (Sandbox Code Playgroud)

这有效......例如:

PS > $omissions = [string[]]@("*.iso","*.pdf","*.zip","*.msi")
**or**
PS > [string[]]$omissions = ("*.iso,*.pdf,*.zip,*.msi").split(',')
PS > $omissions.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String[]                                 System.Array
Run Code Online (Sandbox Code Playgroud)

请注意,即使是“单个”元素仍需要相同的转换,以创建一个 1 元素数组。

如果您在家中尝试此操作,请确保在上面显示的示例中重铸之前使用替换变量“遗漏”清除 $omissions 的存在。

至于我测试过的可靠工作的管道......

--------------------------------------------------------------------------------------- cd $sourcelocation

ls | ?{$_ -ne $null} | ?{$_.BaseName -notmatch "^\.$"} | %{$_.Name} | cp -Destination $targetDir -Exclude $omissions -recurse -ErrorAction silentlycontinue 
---------------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

上面做了基础(选定的“当前”)目录中源文件的目录列表,过滤掉潜在的问题项目,将文件转换为基础名称并强制cp(复制项目别名)重新访问文件“by name”在“当前目录”中 - 从而重新获取文件对象,并复制它。这将创建空目录,包括那些甚至可能包含排除文件的目录(当然少了排除项)。另请注意,“ls”(get-childitem)不会 -recurse - 留给 cp。最后 - 如果您遇到问题并需要调试,请删除 -ErrorAction 静默继续开关和参数,它隐藏了许多可能会中断脚本的麻烦。

对于那些评论与“\”包含相关的人,请记住,您正在通过解释器(即 PowerShell)在 .NET 子层上工作,例如在 c# 中,包含单个“\”(或字符串中的多个单字符),导致编译器要求您通过使用“\\”来转义反斜杠来更正条件,或者在字符串前面加上@,如@“\”;剩下的另一个选项是将字符串括在单引号中,如“\”。所有这一切都是因为像“\n”等字符组合的 ASCII 插值。

后者是一个更大的主题,所以我会让你考虑这个问题。