PowerShell 复制中的排除列表-“项”似乎不起作用

我有以下 PowerShell 脚本片段:

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

它可以将所有文件和文件夹从 t1复制到 t2,但它只排除“ root”/“ first-level”文件夹中的排除列表,而不排除子文件夹中的排除列表。

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

88858 次浏览

我认为最好的方法是使用 Get-ChildItem 并在 Copy-Item 命令中使用管道。

我发现这个方法奏效了:

$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)}

基本上,这里发生的事情是,您将一个一个地浏览有效文件,然后将它们复制到新路径。最后的“ join-Path”语句是为了在复制文件时保留目录。该部分获取目标目录,并将其与源路径后面的目录联接起来。

我从 给你中得到了这个想法,然后对它进行了一些修改,使它能够适用于这个示例。

希望能成功!

我正在寻找一种方法来复制在某个日期/时间戳之后修改的文件,以便对它们进行归档。通过这种方式,我可以精确地保存我正在处理的文件(假设我知道什么时候开始的话)。(是的,我知道这就是 SCM 的用途,但有时候我只是想快照我的工作,而不检入它。)

利用 Landyman 的建议,以及我在其他地方找到的东西,我发现这个方法奏效了:

$source = 'c:\tmp\foo'
$dest = 'c:\temp\foo'
$exclude = @('*.pdb', '*.config')
Get-ChildItem $source -Recurse -Exclude $exclude |
where-object {$_.lastwritetime -gt "8/24/2011 10:26 pm"} |
Copy-Item -Destination {Join-Path $dest $_.FullName.Substring($source.length)}

这个排除参数对 dir 不起作用,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)}

我有一个类似的问题扩展这一点

$source = "D:\scripts\*.sql"

我发现了这个解决方案:

function Copy-ToCreateFolder
{
param(
[string]$src,
[string]$dest,
$exclude,
[switch]$Recurse
)


# The problem with Copy-Item -Rec -Exclude is that -exclude effects only top-level files
# Copy-Item $src $dest    -Exclude $exclude       -EA silentlycontinue -Recurse:$recurse
# http://stackoverflow.com/questions/731752/exclude-list-in-powershell-copy-item-does-not-appear-to-be-working


if (Test-Path($src))
{
# Nonstandard: I create destination directories on the fly
[void](New-Item $dest -itemtype directory -EA silentlycontinue )
Get-ChildItem -Path $src -Force -exclude $exclude | % {


if ($_.psIsContainer)
{
if ($Recurse) # Non-standard: I don't want to copy empty directories
{
$sub = $_
$p = Split-path $sub
$currentfolder = Split-Path $sub -leaf
#Get-ChildItem $_ -rec -name  -exclude $exclude -Force | % {  "{0}    {1}" -f $p, "$currentfolder\$_" }
[void](New-item $dest\$currentfolder -type directory -ea silentlycontinue)
Get-ChildItem $_ -Recurse:$Recurse -name  -exclude $exclude -Force | % {  Copy-item $sub\$_ $dest\$currentfolder\$_ }
}
}
else
{


#"{0}    {1}" -f (split-path $_.fullname), (split-path $_.fullname -leaf)
Copy-Item $_ $dest
}
}
}
}

作为注释格式的代码,我会发布作为回答,但这只是@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)
}
}

Get-ChildItem with join-Path 对我来说大部分时间都很有用,但是我意识到它在复制其他根目录中的根目录,这很糟糕。

比如说

  • C: SomFolder
  • 这里有些文件夹复制
  • 有些文件夹在这里复制东西
  • 在这里复制一些文件夹子文件夹
  • C: Some Folder CopyIn Here 子文件夹 Thin2.txt

  • 来源目录: c: SomFolderCopyInHere

  • 目的地目录: d: PutItIn Here

目标: 将每个子项目拷贝到 d: PutItInHere 的根目录,但不包括 c: somFolderCopyInHere 本身。
- 例如,把所有的孩子在这里复制,使他们的孩子普京在这里

上面的示例基本上都是这样做的,但它创建一个名为 SubFolder 的文件夹,并在文件夹中创建一个名为 SubFolder 的文件夹。

这是因为联接路径计算子文件夹子项的目标路径 d: PutItInHere 子文件夹,所以子文件夹是在名为 SubFolder 的文件夹中创建的。

我通过使用 Get-ChildItems 带回项目的集合,然后使用循环遍历它来解决这个问题。

Param(
[Parameter(Mandatory=$True,Position=1)][string]$sourceDirectory,
[Parameter(Mandatory=$True,Position=2)][string]$destinationDirectory
)
$sourceDI = [System.IO.DirectoryInfo]$sourceDirectory
$destinationDI = [System.IO.DirectoryInfo]$destinationDirectory
$itemsToCopy = Get-ChildItem $sourceDirectory -Recurse -Exclude @('*.cs', 'Views\Mimicry\*')
foreach ($item in $itemsToCopy){
$subPath = $item.FullName.Substring($sourceDI.FullName.Length)
$destination = Join-Path $destinationDirectory $subPath
if ($item -is [System.IO.DirectoryInfo]){
$itemDI = [System.IO.DirectoryInfo]$item
if ($itemDI.Parent.FullName.TrimEnd("\") -eq $sourceDI.FullName.TrimEnd("\")){
$destination = $destinationDI.FullName
}
}
$itemOutput = New-Object PSObject
$itemOutput | Add-Member -Type NoteProperty -Name Source -Value $item.FullName
$itemOutput | Add-Member -Type NoteProperty -Name Destination -Value $destination
$itemOutput | Format-List
Copy-Item -Path $item.FullName -Destination $destination -Force
}

简而言之,它使用当前项的全名进行目标计算。然而,它会检查它是否是一个 DirectoryInfo 对象。如果它检查它的父文件夹是否是源目录,这意味着正在迭代的当前文件夹是源目录的直接子文件夹,因此我们不应该将其名称追加到目标目录,因为我们希望该文件夹在目标目录中创建,而不是在目标目录中的文件夹中创建。

接下来,每个其他文件夹都将正常工作。

$sourcePath="I:\MSSQL\Backup\Full"
$excludedFiles=@("MASTER", "DBA", "MODEL", "MSDB")
$sourceFiles=(ls $sourcePath -recurse -file) | where-object { $_.directory.name -notin $excludedFiles }

这就是我所做的,我需要复制出一些备份文件到网络上的一个单独的位置,以便客户提取。我们不希望他们有上述系统数据库备份。

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

而且一开始就奏效了:

robocopy $source $dest /S /XF <file patterns to exclude> /XD <directory patterns to exclude>

例如:。

robocopy $source $dest /S /XF *.csproj /XD obj Properties Controllers Models

另外,它还有很多功能,比如可恢复复制。 医生 给你

注意,语法规范要求一个 STRING ARRAY; ala STRING []

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

如果在生成数组时没有显式显示,那么最终会得到一个 Object []——在许多情况下,这个对象会被忽略,由于类型安全的原因,留下“ bug 行为”的外观。由于 PowerShell 可以处理脚本块,除了类型特定的变量(这样就可以确定一个有效的字符串)之外的计算将为任何执行策略松散的系统留下一个可能的注入模式攻击的空间。

所以这是不可靠的:

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


Note the result....
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

这个有用... ... 例如:

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

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

如果您正在家中尝试这种方法,请确保在上面的示例中重新转换之前使用替换变量“省略”来清除 $省略的存在。

至于我测试过的可靠的输油管道..。

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


ls | ?{$_ -ne $null} | ?{$_.BaseName -notmatch "^\.$"} | %{$_.Name} | cp -Destination $targetDir -Exclude $omissions -recurse -ErrorAction silentlycontinue
---------------------------------------------------------------------------------------

上面的命令列出了基本目录(选中的“当前”)中的源文件,过滤掉潜在的问题项,将文件转换为基本名,并强制 cp (拷贝项别名)在“工作目录”中“按名称”重新访问该文件——从而重新获取 file 对象,并复制它。这将创建空目录,包括那些甚至可能包含排除的文件(当然排除较少)。还要注意,“ ls”(get-children item)不是递归的,这是留给 cp 的。最后-如果您有问题,需要调试,删除-ErrorAction 静默地继续开关和参数,这隐藏了许多可能中断脚本的干扰。

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

后者是一个更大的主题,所以我将把这个问题留给您考虑。

下面的代码片段将把所有文件和文件夹从 $source复制到 $dest,不包括根文件夹和子文件夹中的 .pdb.config文件:

Get-ChildItem -Path $source | Copy-Item -Destination $dest -Recurse -Container -Exclude @('*.pdb','*.config')

使用正则表达式将项从一个文件夹复制到另一个文件夹以进行排除的一种方法:

$source = '.\source'
$destination = '.\destination'
$exclude = '.*\.pdf$|.*\.mp4$|\\folder1(\\|$)|\\folder2(\\|$)'


$itemsToCopy = Get-ChildItem $source -Recurse |
Where-Object FullName -notmatch $exclude | Select-Object -Expand FullName


$sourceFullNameLength = (Get-Item $source).FullName.Length


foreach ($item in $itemsToCopy) {
$relativeName = $item.Substring($sourceFullNameLength + 1)
Copy-Item -Path $item -Destination "$destination\$relativeName"
}

我为日常使用编写了这个代码,并将其打包在脚本模块中,它维护所有的目录结构并支持通配符:

function Copy-Folder {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[String]$FromPath,


[Parameter(Mandatory)]
[String]$ToPath,


[string[]] $Exclude
)


if (Test-Path $FromPath -PathType Container) {
New-Item $ToPath -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
Get-ChildItem $FromPath -Force | ForEach-Object {
# avoid the nested pipeline variable
$item = $_
$target_path = Join-Path $ToPath $item.Name
if (($Exclude | ForEach-Object { $item.Name -like $_ }) -notcontains $true) {
if (Test-Path $target_path) { Remove-Item $target_path -Recurse -Force }
Copy-Item $item.FullName $target_path
Copy-Folder -FromPath $item.FullName $target_path $Exclude
}
}
}
}

打电话给 Copy-Folder -FromPath 'fromDir' -ToPath 'destDir' -Exclude *.pdb,*.config

-FromPath-ToPath可以省略,

Copy-Folder -FromPath 'fromDir destDir -Exclude *.pdb,*.config