检查文件扩展名是否以这个或那个结尾的最有效方法?

Most efficient way to check if file extension ends in this OR that?

我有这段代码,我非常满意:

$jpgExtensionFiles = New-Object System.Collections.ArrayList

$AllDrives = Get-PSDrive
  foreach ($Drive in $AllDrives) {
      $Dirs = Get-ChildItem $Drive.Root -Recurse -ErrorAction SilentlyContinue
      $jpgExtensionFiles.Add(($Dirs | where {$_.extension -eq ".jpg"}))         
   }

但是现在,我想对一个名为 $bmpExtensionFiles 的数组做同样的事情。我不知道如何执行以下操作:

  1. 如果扩展名是.jpg,添加到第一个数组
  2. 如果扩展名是.bmp,添加到第二个数组

...为了效率起见,全部在一个循环中。我知道我可以只添加 $bmpExtensionFiles.Add(($Dirs | where {$_.extension -eq ".bmp"})),但有没有更有效的方法?我需要它与 PowerShell v2 兼容。

你可以使用 switch 语句,然后如果你想在以后添加更多类型,它很容易扩展:

ForEach ($Dir in $Dirs)
{
    Switch ($Dir.Extension)
    {
        ".jpg" {$jpgExtensionFiles.Add($Dir)}
        ".bmp" {$bmpExtensionFiles.Add($Dir)}
    }
}

没有广泛测试,但应该非常有效。

$jpgExtensionFiles,$bmpExtensionfiles = (Get-ChildItem $Drive.Root -Recurse -include *.jpg,*.bmp -ErrorAction SilentlyContinue).where({$_.extension -eq '.jpg'},'split')

以下是 hashtable-based 解决方案 推广到任意数量的扩展


Multi-pass 解决方案使用 Get-ChildItem -Filter:

# Initialize a hashtable for all extexnsions of interest:
# Key is the extension name, value is an array list to be filled
# with the files ([System.IO.FileInfo] instances) having that extension.
$htFilesByType = @{ 
  '.jpg' = New-Object System.Collections.ArrayList
  '.bmp' = New-Object System.Collections.ArrayList 
}

# Get the list of all filesystem PS drives.
$AllFsDrives = Get-PSDrive -PSProvider FileSystem

# Loop over all filesystem drives.
foreach ($Drive in $AllFsDrives) {
  # Loop over all extensions and add files with that extension to the hashtable.
  foreach ($ext in $htFilesByType.Keys) {
    # Get all files with the extension at hand at once and add them
    # to the relevant hashtable entry.
    # Note: The .Add() method returns a value, which we're not interested in,
    #       hence the `$null = ...`.
    $null = $htFilesByType[$ext].Add(
      (Get-ChildItem -Filter "*$ext" $Drive.Root -Recurse -ErrorAction SilentlyContinue)
    )
  }
}

# Output the filled hashtable.
$htFilesByType

注意:乍一看,运行 Get-ChildItem -Recurse -Filter <ext> for each 扩展似乎效率低下,但对于 small这可能仍然比下面的 single-pass Get-ChildItem -Recurse -Include <ext1>, <ext2>, ... 解决方案快:

  • -Filter 参数是 drive-provider-native 并在 source 处过滤,这使得它比 [=16] 更有效=] 参数.

  • 不过遗憾的是,-Filter只支持一个单个过滤器(通配符表达式如*.jpg),这就是为什么多个需要通行证。

另请注意,为简单起见,在每次传递中首先收集整个匹配文件数组,然后将其作为一个整体添加到哈希表中。这样快,也memory-consuming;如果这是一个问题,请按照下面的 single-pass 解决方案逐一处理文件。

很难预测 single-pass 解决方案变得更快的确切点,因为涉及许多因素(匹配文件与 non-matching 的比率)文件,通过次数,...)。
如有疑问,请使用 Measure-Command 比较两个解决方案。


单次-使用multi-filterGet-ChildItem -Include命令的解决方案:

# Construct the array of wildcard expressions to use with -Include;
# e.g., @( '*.jpg', '*.bmp' )
$includes = $htFilesByType.keys -replace '^', '*'

foreach ($Drive in $AllFsDrives) {
  # Look for files with any of the extensions of interest.
  Get-ChildItem -Include $includes $Drive.Root -Recurse -ErrorAction SilentlyContinue |
    ForEach-Object {
      # Add to the appropriate array list based on the matching file's extension. 
      $null = $htFilesByType[$_.Extension].Add($_)
    }
}  

注意:在 PSv3 或更高版本中,您可以通过添加 -File 开关来限制仅匹配文件,从而稍微提高 Get-ChildItem 命令的效率。