如何使用 Get-ChildItem -exclude 排除多个文件夹?

Posted

技术标签:

【中文标题】如何使用 Get-ChildItem -exclude 排除多个文件夹?【英文标题】:How can I exclude multiple folders using Get-ChildItem -exclude? 【发布时间】:2013-02-24 01:07:51 【问题描述】:

我需要为我们的 Pro/Engineer CAD 系统生成一个配置文件。我需要来自我们服务器上特定驱动器的文件夹的递归列表。但是我需要排除任何包含“ARCHIVE”的文件夹,包括各种不同的情况。

我已经编写了以下内容,但它不排除文件夹!

$folder = "T:\Drawings\Design\*"
$raw_txt = "T:\Design Projects\Design_Admin\PowerShell\raw.txt"
$search_pro = "T:\Design Projects\Design_Admin\PowerShell\search.pro"
$archive = *archive*,*Archive*,*ARCHIVE*

Get-ChildItem -Path $folder -Exclude $archive -Recurse  | where $_.Attributes -match 'Directory'  | ForEach-Object $_.FullName > $search_pro   

【问题讨论】:

peterjfrancis,根据您的一位 cmets,@CB 提供的答案是排除名称中包含“ARCHIVE”文件夹的路径的正确方法。 CB 的答案应该被标记为正确。 【参考方案1】:

我需要排除特定路径,而不仅仅是树中任何位置的同名目录,所以我在 Jason Brower's answer 上构建以匹配目录路径而不是它们的名称。

Get-Childitem filespec -Recurse | Where-Object $_ -excludecondition 之类的解决方案确实有效,但它们会不必要地查看已排除的文件夹,然后再将其关闭,这可能会变得昂贵。 (使用管道:“左过滤,右处理”)

$strSearchPath = 'D:\Project'
# Files to search for
$arFilePatterns = @(
    '*.ps?',
    '*.cmd'
)

# Directories to skip
# Example: you got 'Archive', 'Archive.old', 'Archive.bak' and want to include only 'Archive' in the search

# (think) exact matches
$arSkipDirs = @(
    'D:\Project\Archive.old',
    'D:\Project\Archive.bak'
)
# (think) wildcard to the right
<#
$arSkipDirs = @(
    'D:\Project\Archive.'
)
#>

Function ListDirsSkipSome ($strPath, $strExcludeRegEx) 
    Get-ChildItem -Path $strPath -Directory | 
    ForEach-Object 
        if ($_.FullName -inotmatch $strExcludeRegEx) 
            # recurse down the tree
            ListDirsSkipSome $_.FullName $strExcludeRegEx
            return $_.FullName
        
    


#Build a regex using array of excludes
# exact matches
$strRegEx = '^0$' -f (($arSkipDirs | ForEach-Object  [regex]::Escape($_) ) -join ('$|^'))
# wildcards to the right
#$strRegEx = '^0' -f (($arSkipDirs | ForEach-Object  [regex]::Escape($_) ) -join ('|^'))

# include root of search path
$arSearchDirs = @($strSearchPath)
# add list of directories excluding some
$arSearchDirs += ListDirsSkipSome $strSearchPath $strRegEx

# save current directory
$strPWD = (Get-Location).Path

# find files in listed dirs
# set type in case there is only 1 result
[array]$arSearchResult = $arSearchDirs |
ForEach-Object 
    # dive into each directory
    Set-Location -Path $_
    # list files matching patterns
    (Get-ChildItem -File -Path $arFilePatterns).FullName


# return to previous directory
Set-Location -Path $strPWD

$arSearchResult

【讨论】:

Jason Brower 的回答应该因其效率而受到极高的评价,但我既不能评论其他人的帖子也不能投票【参考方案2】:

我就是这样做的:

Get-ChildItem -Recurse -Name | ? $_ -notmatch 'node_modules' 

这会递归地列出每个文件的完整路径,这些文件的路径中不包含 node_modules。 您显然应该使用要过滤的任何字符串更改 node_modules

【讨论】:

【参考方案3】:

我认为最简单的形式。对全名使用 -NotMatch。是的,它需要最新版本的 PowerShell,因为我使用 -Directory。

$folder = "T:\Drawings\Design\*"
$search_pro = "T:\Design Projects\Design_Admin\PowerShell\search.pro"
$archive = 'archive'

Get-ChildItem -Path $folder -Directory | Where-Object Fullname -NotMatch $archive | Select-Object Fullname | Out-File $search_pro

【讨论】:

【参考方案4】:

这是另一种使用远程服务器的方法。这里的任务是获取文件夹列表,但不包括远程服务器 C: 驱动器上的一些众所周知的文件夹。最后一个变量 $AllFolders 包含结果。

$Server          = "<ServerName>"
$TopFolder       = "\\$Server\C$"    
$Exclude         = @("*Inetpub*")
$Exclude        += "*Program Files*"
$Exclude        += "*Program Files (x86)*"
$Exclude        += "*Windows*"

$ServerFolders   = Get-ChildItem -Path $TopFolder -Exclude $Exclude | where $_.PSIsContainer 
ForEach ($ServerFolder in $ServerFolders)

 $CurrentFolders = Get-ChildItem -path $ServerFolder -recurse | Where-Object  $_.PSIsContainer  
 $AllFolders     = $AllFolders + $CurrentFolders

【讨论】:

【参考方案5】:
$CurrentPath = (Get-Location -PSProvider FileSystem).ProviderPath # Or your favorite path
$IncludeNames = "okFolder1", "okFolder2"  # Items names to search
$ExcludeNames = "koFolder1", "koFolder2"  # Items names not to search
$depth = 3                                # Max level of depth to search

$FoldersToRemove = Get-ChildItem .\ -include $IncludeNames -Recurse -Depth $depth 
-Attributes D                             # If you want only directories, change it as you desire
| ?  $_.fullname -inotmatch ($ExcludeNames -join '|')   #case insensitive or use -cnotmatch for case sensitive comparison
| foreach $_.fullname                   # If you want to project only full path information of matches

【讨论】:

【参考方案6】:

我会这样做:

Get-ChildItem -Path $folder -r  | 
?  $_.PsIsContainer -and $_.FullName -notmatch 'archive' 

注意-notmatch 接受正则表达式:

https://docs.microsoft.com/powershell/module/microsoft.powershell.core/where-object#parameters

【讨论】:

这行得通,但是基于gci 中的名称进行排除不是更好吗?(过滤器左,格式右)? 就性能而言,我认为是这样,但 IMO 我使用正则表达式可以更好地控制要排除的内容..但这只是个人喜好.. 也许这是一个错误。似乎“-Exclude”选项仅适用于文件夹或文件,而不适用于路径。 对我来说最好的解决方案。我喜欢较短的版本:gci -r | ? fullname -notm 'archive' @George2.0希望如果您在 PowerShell 中键入 alias,您将获得其所有当前别名的打印输出,其中显示 ? -&gt; Where-Object。所以,是的,?Where-Object 的别名。不知道为什么他们使用别名而不是全名,因为他们使用的是 Get-ChildItem 而不是 gci【参考方案7】:

您也可以在单个语句中执行此操作:

$j = "Somepath"
$files = Get-ChildItem -Path $j -Include '*.xlsx','*.zip' -Recurse -ErrorAction SilentlyContinue –File | ? $_.Directory -notlike "$j\donotwantfoldername"

【讨论】:

您好,欢迎来到 Stack Overflow!请拨打tour。感谢您的回答,但您是否还可以添加有关您的代码如何解决问题的解释?查看help center 了解如何格式化代码。【参考方案8】:

对我来说最简单的简短形式是这样的:

#find web forms in my project except in compilation directories
(gci -recurse -path *.aspx,*.ascx).fullname -inotmatch '\\obj\\|\\bin\\'

如果您需要更复杂的逻辑,请使用过滤器:

  filter Filter-DirectoryBySomeLogic
  
      param(
      [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
      $fsObject,
      [switch]$exclude
      )
          
      if($fsObject -is [System.IO.DirectoryInfo])
      
          $additional_logic = $true ### replace additional logic here
  
          if($additional_logic)
              if(!$exclude) return $fsObject 
          
          elseif($exclude) return $fsObject 
      
          
  
  
  gci -Directory -Recurse | Filter-DirectoryBySomeLogic | ....

【讨论】:

【参考方案9】:

我知道这已经很老了 - 但在寻找一个简单的解决方案时,我偶然发现了这个线程...... 如果我的问题是正确的,那么您正在寻找一种使用 Get-ChildItem 列出多个目录的方法。使用 powershell 5.0 似乎有一种更简单的方法 - 示例

Get-ChildItem -Path D:\ -Directory -Name -Exclude tmp,music
   chaos
   docs
   downloads
   games
   pics
   videos

如果没有 -Exclude 子句,tmp 和 music 仍会在该列表中。如果您不使用 -Name,则 -Exclude 子句将不起作用,因为 Get-ChildItem 的详细输出。 希望这可以帮助一些正在寻找一种简单方法来列出所有目录名称而不列出某些目录名称的人。

【讨论】:

由于参数是 Name 而不是 FullName,这不适用于需要排除叶子名称以外的东西的递归情况。【参考方案10】:

我想要一个不涉及循环遍历每个项目并执行ifs 的解决方案。这是一个解决方案,它只是Get-ChildItem 上的一个简单递归函数。我们只是循环和递归目录。


function Get-RecurseItem 
    [Cmdletbinding()]
    param (
        [Parameter(ValueFromPipeline=$true)][string]$Path,
        [string[]]$Exclude = @(),
        [string]$Include = '*'
    )
    Get-ChildItem -Path (Join-Path $Path '*') -Exclude $Exclude -Directory | ForEach-Object 
        @(Get-ChildItem -Path (Join-Path $_ '*') -Include $Include -Exclude $Exclude -File) + ``
        @(Get-RecurseItem -Path $_ -Include $Include -Exclude $Exclude)
    

【讨论】:

【参考方案11】:
#For brevity, I didn't define a function.

#Place the directories you want to exclude in this array.
#Case insensitive and exact match. So 'archive' and
#'ArcHive' will match but 'BuildArchive' will not.
$noDirs = @('archive')

#Build a regex using array of excludes
$excRgx = '^0$' -f ($noDirs -join ('$|^'))

#Rather than use the gci -Recurse option, use a more
#performant approach by not processing the match(s) as
#soon as they are located.
$cmd = 
  Param([string]$Path)
  Get-ChildItem $Path -Directory |
  ForEach-Object 
    if ($_.Name -inotmatch $excRgx) 
      #Recurse back into the scriptblock
      Invoke-Command $cmd -ArgumentList $_.FullName;
      #If you want all directory info change to return $_
      return $_.FullName
    
  


#In this example, start with the current directory
$searchPath = .
#Start the Recursion
Invoke-Command $cmd -ArgumentList $searchPath

【讨论】:

【参考方案12】:

基于对@Guillem 答案的@NN_ 评论,我想出了以下代码。这允许您排除文件夹和文件:

Get-ChildItem -Exclude 'folder-to-exclude','second-folder-exclude' |
foreach 
    Get-ChildItem -Path $_ -Exclude 'files-to-exclude','*.zip','*.mdmp','*.out*','*.log' -Recurse |
    Select-String -Pattern 'string-to-look-for' -List

【讨论】:

【参考方案13】:

VertigoRay 在他的回答中解释说 -Exclude 仅在路径的叶级别起作用(对于文件,路径被剥离的文件名;对于子目录,路径被剥离的目录名)。所以看起来 -Exclude 不能用于指定目录(例如“bin”)并排除该目录中的所有文件和子目录。

这是一个排除一个或多个目录的文件和子目录的函数(我知道这不是直接回答问题,但我认为它可能有助于绕过 -Exclude 的限制):

$rootFolderPath = 'C:\Temp\Test'
$excludeDirectories = ("bin", "obj");

function Exclude-Directories

    process
    
        $allowThrough = $true
        foreach ($directoryToExclude in $excludeDirectories)
        
            $directoryText = "*\" + $directoryToExclude
            $childText = "*\" + $directoryToExclude + "\*"
            if (($_.FullName -Like $directoryText -And $_.PsIsContainer) `
                -Or $_.FullName -Like $childText)
            
                $allowThrough = $false
                break
            
        
        if ($allowThrough)
        
            return $_
        
    


Clear-Host

Get-ChildItem $rootFolderPath -Recurse `
    | Exclude-Directories

对于目录树:

C:\Temp\Test\
|
├╴SomeFolder\
|  |
|  └╴bin (file without extension)
|
└╴MyApplication\
  |
  ├╴BinFile.txt
  ├╴FileA.txt
  ├╴FileB.txt
  |
  └╴bin\
    |
    └╴Debug\
      |
      └╴SomeFile.txt

结果是:

C:\Temp\Test\
|
├╴SomeFolder\
|  |
|  └╴bin (file without extension)
|
└╴MyApplication\
  |
  ├╴BinFile.txt
  ├╴FileA.txt
  └╴FileB.txt

它排除 bin\ 子文件夹及其所有内容,但不排除文件 Bin.txt 或 bin(名为“bin”的文件,没有扩展名)。

【讨论】:

这个问题是 Get-ChildItem 已经花费了 XX 时间递归目录,并且只有在它完成之后才进行过滤。如果重点是避免费用搜索(几乎总是如此),那么这将无济于事。【参考方案14】:

KISS 跳过某些文件夹的方法是链接 Get-ChildItem 调用。如果您想要的话,这会排除根级文件夹,但不包括更深层次的文件夹。

Get-ChildItem -Exclude folder1,folder2 | Get-ChildItem -Recurse | ...
开始排除你不想要的文件夹 然后进行递归搜索,排除不需要的文件夹。

我喜欢这种方法的原因是它简单易记。如果您不想在第一次搜索中混合文件夹和文件,则需要使用过滤器。

【讨论】:

我以双 foreach 结束处理文件和文件夹 Get-ChildItem -Exclude folder1,folder2 | foreach Get-ChildItem -Path $_ -Include *.a -Exclude *.b -Recurse | foreach 回声 $_ 。有什么更好的吗? 这是问题的正确答案,应该被接受。唯一真正阻止 Get-ChildItem 进行昂贵且长递归的方法。其他答案允许发生不必要的递归并在事后过滤。谢谢!【参考方案15】:

您可以像这样排除正则表达式“或”符号,假设您想要的文件与您要排除的文件夹的名称不同。

$exclude = 'dir1|dir2|dir3'
ls -r | where  $_.fullname -notmatch $exclude 

ls -r -dir | where fullname -notmatch 'dir1|dir2|dir3'

【讨论】:

【参考方案16】:

在您的情况下,您可以通过以下方式达到此目的:

    mv excluded_dir ..\
    ls -R 
    mv ..\excluded_dir .

【讨论】:

移动文件夹是解决无关问题的危险方法。如果移动没有移动隐藏/系统文件,或者脚本在两个移动操作之间终止(由用户或崩溃),这可能会产生严重的副作用。【参考方案17】:

如果这个答案似乎与以前的答案重复,我深表歉意。我只是想展示一个更新的(通过 POSH 5.0 测试)解决这个问题的方法。以前的答案是 3.0 之前的版本,不如现代解决方案高效。

The documentation 对此并不清楚,但Get-ChildItem -Recurse -Exclude 仅匹配叶 (Split-Path $_.FullName -Leaf) 上的排除,而不是父路径 (Split-Path $_.FullName -Parent)。匹配排除只会删除具有匹配叶子的项目; Get-ChildItem 仍将递归到该叶子中。

在 POSH 1.0 或 2.0 中

Get-ChildItem -Path $folder -Recurse  | 
          ?  $_.PsIsContainer -and $_.FullName -inotmatch 'archive' 

注意: 相同answer as @CB。

在 POSH 3.0+ 中

Get-ChildItem -Path $folder -Directory -Recurse  | 
          ?  $_.FullName -inotmatch 'archive' 

注意: 已更新answer from @CB。

多重排除

这专门针对目录,同时排除带有 Exclude 参数的叶子,以及带有 ilike(不区分大小写)比较的父级:

#Requires -Version 3.0
[string[]]$Paths = @('C:\Temp', 'D:\Temp')
[string[]]$Excludes = @('*archive*', '*Archive*', '*ARCHIVE*', '*archival*')

$files = Get-ChildItem $Paths -Directory -Recurse -Exclude $Excludes | % 
    $allowed = $true
    foreach ($exclude in $Excludes)  
        if ((Split-Path $_.FullName -Parent) -ilike $exclude)  
            $allowed = $false
            break
        
    
    if ($allowed) 
        $_
    

注意:如果您希望$Excludes 区分大小写,有两个步骤:

    Get-ChildItem 中删除Exclude 参数。 将第一个if 条件更改为: if ($_.FullName -clike $exclude)

注意:此代码具有我永远不会在生产中实现的冗余。您应该稍微简化一下以适应您的确切需求。它可以作为一个详细的示例。

【讨论】:

我不知道为什么我被否决了。也许是因为第一部分的答案与@CB 相同? I tried to just edit his answer,但编辑被拒绝,原因如下:“此编辑旨在解决帖子的作者,作为编辑没有意义。它应该写成评论或答案。” 我相信@CB 提供的答案将适用于所有版本的 PowerShell 并回答了 OPs 的问题。 POSH 3.0+ 示例更适合作为对 CB 帖子的简短评论。 @jaylweb 我从来没有说过它不起作用......我只是说 CB 的答案在 POSH 3.0+ 中效率不高。我还想添加关于多个排除的位,即使 OP 的多个排除基本上相同的ilike 字符串,但也许他不想排除“ArcHive”,因此他应该使用clike ...我添加了一个注释。所有这些更改都会导致冗长且格式不正确的评论。正如我已经说过的,它没有被批准为编辑......所以一个新的答案诞生了。感谢您的反馈! 值得一票作为解释正在发生的事情的唯一答案:排除仅在路径的叶级别(即路径中的最后一个文件名或目录名称)上运行,而不是在路径上作为一个整体。 @PeterMcEvoy 我认为您误读了代码。 break 正在破坏:foreach ($exclude in $Excludes) 不是管道。在foreach循环之后,我输出了当前的管道项; if ($allowed) ....这段代码的结果已经过全面测试,我正在生产中使用。但是,如果您碰巧发现了破坏它的边缘情况,请告诉我。【参考方案18】:

排除模式应该不区分大小写,因此您不必为排除指定每个大小写。

也就是说,-Exclude 参数接受一个字符串数组,所以只要你这样定义$archive,你就应该被设置。

$archive = ("*archive*","*Archive*","*ARCHIVE*");

您还应该从 $folder 中删除尾随星号 - 因为您指定了 -recurse,所以您只需要提供***文件夹。

$folder = "T:\Drawings\Design\"

完全修改的脚本。这也会改变您检测是否找到目录的方式,并跳过Foreach-Object,因为您可以直接拉取属性并将其全部转储到文件中。

$folder = "T:\Drawings\Design\";
$raw_txt = "T:\Design Projects\Design_Admin\PowerShell\raw.txt";
$search_pro = "T:\Design Projects\Design_Admin\PowerShell\search.pro";
$archive = ("*archive*","*Archive*","*ARCHIVE*");

Get-ChildItem -Path $folder -Exclude $archive -Recurse  | where $_.PSIsContainer  | select-Object -expandproperty FullName |out-file $search_pro 

【讨论】:

$archive = ("*archive*","*Archive*","*ARCHIVE*")$archive = "*archive*","*Archive*","*ARCHIVE*"$archive = @("*archive*","*Archive*","*ARCHIVE*") 是等价的。 那我觉得你需要用@C.B.的回答。 终于解决了! Get-ChildItem -Path $folder -Recurse | ? $_.psiscontainer -and $_.fullname -notmatch 'archive' | select-Object -expandproperty FullName | out-file $search_pro这似乎工作感谢您的帮助@alroc & @C.B. 您可以使用-Directory 参数来获取目录,而不是检查生成的对象是否为容器。 -exclude only applies to the name of an item 所以这将工作。正如接受的答案中所建议的那样,您需要将结果通过管道传输到 Where(别名为 ?),以便过滤 FullName。 @alroc - 这可能是获得disciplined 徽章的好日子。

以上是关于如何使用 Get-ChildItem -exclude 排除多个文件夹?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PowerShell 的 Get-ChildItem -Exclude cmdlet 中对目录使用通配符

如何Get-Childitem 只包含子文件夹和文件?

使用长文件路径时的 Get-ChildItem 错误处理

PowerShell > Get-ChildItem > MissingArgument |总菜鸟

“[”和“]”字符弄乱了get-childitem

添加 Get-ChildItem 列表的文件大小