我有以下PowerShell脚本片段:
$source = 'd:\t1\*'
$dest = 'd:\t2'
$exclude = @('*.pdb','*.config')
Copy-Item $source $dest -Recurse -Force -Exclude $exclude
它可以将所有文件和文件夹从t1复制到t2,但仅排除“ root” /“ first-level”文件夹中的排除列表,而不是子文件夹中的排除列表。
如何使其排除所有文件夹中的排除列表?
Answers:
我认为最好的方法是在Copy-Item命令中使用Get-ChildItem和管道。
我发现这可行:
$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”语句是为了在复制文件时也保留目录。该部分采用目标目录,并将其与源路径之后的目录连接。
我从这里得到的想法,然后对其进行了一些修改以使其适用于本示例。
我希望它能起作用!
我也遇到了这个问题,花了20分钟在这里应用解决方案,但是仍然遇到问题。
所以我选择使用robocopy-好的,它不是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
此外,它还具有大量功能,例如可恢复副本。文档在这里。
robocopy
也是我的最爱,但请注意您的脚本是否依赖退出代码。参见blogs.technet.microsoft.com/deploymentguys/2008/06/16/…–
由于注释的格式很糟糕,因此我将其张贴为答案,但这只是@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)
}
}
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)}
请注意,语法规范要求使用“字符串数组”;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 []-在许多情况下会被忽略,由于类型安全,会出现“笨拙的行为”。由于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元素数组。
如果您在家中尝试此操作,请确保使用Replace-Variable“遗漏”清除$ omissions的存在,然后在上述示例中重铸它。
至于我测试过的可靠运行的管道...
--------------------------------------------------------------------------------------- cd $sourcelocation
ls | ?{$_ -ne $null} | ?{$_.BaseName -notmatch "^\.$"} | %{$_.Name} | cp -Destination $targetDir -Exclude $omissions -recurse -ErrorAction silentlycontinue
---------------------------------------------------------------------------------------
上面的操作在基本(选定的“当前”)目录中列出了源文件的目录,过滤出潜在的问题项,将文件转换为基本名称,并强制cp(复制项别名)重新访问文件“当前目录”中的“名称”-从而重新获取文件对象,然后将其复制。这将创建空目录,包括甚至可能包含排除文件的目录(当然要包括排除项)。还请注意,“ ls”(get-childitem)不会-递归-留给cp。最后-如果遇到问题并需要调试,请删除-ErrorAction静默继续开关和参数,这将隐藏许多可能会干扰脚本的麻烦。
对于那些注释与“ \”包含项有关的注释,请记住,您正在通过解释器(即PowerShell)在.NET子层上工作,例如在c#中,包含单个“ \”(或字符串中的多个单打),导致编译器要求您使用“ \\”来转义反斜杠,或者在字符串前加上@,例如@“ \”来更正条件;剩下的另一个选项是用单引号将字符串括起来,如'\'。所有这些都是由于字符组合(例如“ \ n”等)的ASCII插值。
后者是一个更大的主题,因此我将考虑这一点。
我正在寻找一种方法来复制在特定日期/时间戳后修改的文件,以便将其存档。这样,我就可以准确地保存工作的文件(假设我知道什么时候开始)。(是的,我知道这就是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)}
带有Join-Path的Get-ChildItem最适合我,但是我意识到它正在将根目录复制到其他根目录中,这很糟糕。
例如
c:\ SomeFolder \ CopyInHere \ SubFolder \ Thin2.txt
源目录:c:\ SomeFolder \ CopyInHere
目标:将c:\ SomeFolder \ CopyInHere中的每个子项复制到d:\ PutItInHere的根目录,但不包括c:\ SomeFolder \ CopyInHere本身。
-例如,将CopyInHere的所有子级都作为它们的PutItInHere的子级
上面的示例在大多数情况下都执行此操作,但是会发生什么情况:它创建一个名为SubFolder的文件夹,并在名为SubFolder的文件夹中创建一个文件夹。
这是因为Join-Path为SubFolder子项计算了d:\ PutItInHere \ SubFolder的目标路径,因此在名为SubFolder的文件夹中创建了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对象。如果是它,则检查其父文件夹是否是源目录,这意味着要迭代的当前文件夹是源目录的直接子目录,因此我们不应将其名称附加到目标目录,因为我们希望该文件夹为在目标目录中创建,而不是在目标目录中的文件夹中。
然后,其他所有文件夹都可以正常工作。
我有一个类似的问题,扩大这一点。我想要适用于以下来源的解决方案
$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
}
}
}
}