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

79

我需要为我们的Pro/Engineer CAD系统生成一个配置文件。我需要一个递归列表,列出服务器上特定驱动器中的所有文件夹。但是,我需要排除包含'ARCHIVE'的任何文件夹,包括不同大小写的情况。

我已经编写了以下代码,它可以列出所有文件夹,但是没有排除包含'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,根据您的一条评论,@CB提供的答案是排除包含“ARCHIVE”名称文件夹路径的正确方法。应将CB的答案标记为正确。 - jaylweb
18个回答

76

我跳过某些文件夹的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 { echo $_ } }。有更好的方法吗? - NN_
这是问题的正确答案,应该被接受。它是唯一一个真正防止 Get-ChildItem 进行昂贵和长递归的方法。其他答案允许不必要的递归发生,并在事后进行过滤。谢谢! - TugboatCaptain
2
如果在第二个“Get-ChildItem”上添加“-Filter”,它不会过滤掉第一个“Get-ChildItem”调用的根目录中的文件,最终你会得到意外的文件。 - Silex
2
@Silex 说得好。-Filter 在这种方法下无法工作。 - guillem

55

这个可以运行,但是基于名称在 gci 中进行排除(过滤左侧,格式化右侧)不是更好吗? - alroc
1
就性能而言,我认为是这样,但在我看来,使用正则表达式可以更好地控制要排除的内容.. 但这只是个人偏好.. - CB.
8
也许这是一个错误。看起来选项“-Exclude”只适用于文件夹或文件,而不是路径。 - Bohr
对我来说,最好的解决方案是使用更简短的版本:gci -r | ? fullname -notm 'archive' - Rasmond
1
@George2.0希望如果您在PowerShell中键入“alias”,则会打印出所有当前别名,其中显示“? -> Where-Object”。因此,“?”是“Where-Object”的别名。不确定为什么他们使用了别名而不是全名,考虑到他们使用了“Get-ChildItem”而不是“gci”。 - Jonathan E. Landrum
显示剩余3条评论

54

如果我的回答看起来像之前的答案的重复,我只是想展示一种更新的解决方法(在POSH 5.0测试通过)。之前的答案都是针对3.0之前的版本,不像现代解决方案那样高效。

这份文档并没有明确说明,但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' }

注意:与@CB的答案相同

在POSH 3.0及以上版本中

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

注意:来自@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区分大小写,需要进行两个步骤:

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

注意:此代码具有冗余性,在生产环境中我不会这样实现。您应该根据自己的需要对其进行简化。它作为详细示例使用得很好。


2
我不知道为什么被踩了。也许是因为第一部分和@CB的答案相同?我试图编辑他的答案,但是这个编辑被拒绝了,理由是:“这个编辑意在回应帖子作者,作为编辑没有任何意义。它应该被写成评论或答案。” - VertigoRay
我相信@CB提供的答案适用于所有版本的PowerShell,并回答了OP的问题。POSH 3.0+示例最好作为对CB帖子的简短评论。 - jaylweb
1
@jaylweb 我从未说过它不会工作...我只是说CB的答案在POSH 3.0+中不够高效。我还想补充一下关于多个排除项的内容,即使OP的多个排除项本质上是相同的ilike字符串,但也许他不想排除"ArcHive",因此他应该使用clike...我已经添加了一个注释。所有这些更改都会导致一个冗长、格式不良好的评论。正如我已经说过的,它没有被批准作为编辑...所以一个新的答案诞生了。感谢您的反馈! - VertigoRay
8
值得点赞,因为它是唯一解释发生情况的回答:Exclude 只作用于路径的叶子级别(即路径中的最后一个文件名或目录名),而不是整个路径。 - Simon Elms
我不确定在管道中使用“break”是否能实现您想要的效果(转到管道中的下一项)。相反,我认为您需要使用“return”。 - Peter McEvoy
1
@PeterMcEvoy 我觉得你误读了代码。 break 是用来中断 foreach ($exclude in $Excludes) { 循环而不是管道。在 foreach 循环之后,我输出当前的管道项;if ($allowed) ...。这段代码的结果经过了彻底的测试,并且我正在生产中使用它。然而,如果你恰巧发现一个破坏它的边缘情况,请让我知道。 - VertigoRay

15
排除模式应该不区分大小写,因此您不需要为排除指定每个情况。
话虽如此,“-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 

2
$archive = ("archive","Archive","ARCHIVE") 和 $archive = "archive","Archive","ARCHIVE" 和 $archive = @("archive","Archive","ARCHIVE") 是等价的。 - CB.
1
那么我认为你需要使用@C.B.的答案。 - alroc
3
终于解决了!Get-ChildItem -Path $folder -Recurse | ? {$_.psiscontainer -and $_.fullname -notmatch 'archive'} | select-Object -expandproperty FullName | out-file $search_pro。看起来这样可以运行。感谢@alroc和@C.B.提供的帮助。 - peterjfrancis
1
你可以使用-Directory参数来获取目录,而不是检测你的结果对象是否为容器。 - jrsconfitto
11
“-exclude”仅适用于项的名称,因此这种方法不会起作用。如已接受答案所建议的那样,您需要将结果导入“Where”(别名为“?”)以便根据“FullName”进行过滤。@alroc - 也许今天是获得disciplined徽章的好日子。 - KyleMit
显示剩余9条评论

10

我知道这篇文章已经相当老了-但在寻找一个简单的解决方案时,我偶然发现了这个帖子...... 如果我理解问题正确,您正在寻找一种使用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,在需要排除叶子名称以外的其他内容的递归情况下,这种方法无法奏效。 - Timo Riikonen

5
这是我做的方法:
Get-ChildItem -Recurse -Name | ? {$_ -notmatch 'node_modules' }

此命令会递归列出所有不包含“node_modules”字符串的文件路径。

你可以将“node_modules”替换为任何你想要过滤的字符串。


4
你可以像这样排除,使用正则表达式中的“或”符号,假设你要排除的文件名与你要排除的文件夹名称不同。
$exclude = 'dir1|dir2|dir3'
ls -r | where { $_.fullname -notmatch $exclude }

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

3
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”的文件)。

1
问题在于Get-ChildItem已经花费了XX的时间递归目录,只有在完成后才进行筛选。如果目的是避免昂贵的搜索(这几乎总是情况),那么这并没有帮助。 - TugboatCaptain

3

对我来说,最简单的表达方式是这样的:

#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 | ....

如果你正在寻找文件夹中的文件(这不完全是上面的问题,但是这是我的情况),那么fullname -inotmatch '\\obj\\|\\bin\\'这部分真的值得一提! - tomwaitforitmy

2

我认为这是最简单的形式。在 Fullname 上使用 -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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接