在Powershell中过滤CSV中的搜索结果

时间:2013-12-03 21:00:03

标签: arrays powershell filtering

我对如何编写搜索文件系统以查找与主CSV文件中包含的任何条目匹配的文件的一些想法感兴趣。我有一个搜索文件系统的功能,但是对CSV的过滤比我预期的更难。我有一个带有标题的csv用于Name& IPaddr的:

#create CSV object
$csv = import-csv filename.csv

#create filter object containing only Name column
$filter = $csv | select-object Name

#Now run the search function 
SearchSubfolders | where {$_.name -match $filter} #returns no results

我想我的问题是这样的:我可以过滤像这样的管道中的数组吗?

5 个答案:

答案 0 :(得分:1)

你需要一对循环:

#create CSV object
$csv = import-csv filename.csv

#Now run the search function
#loop through the folders
foreach ($folder in (SearchSubfolders)) {
    #check that folder against each item in the csv filter list
    #this sets up the loop
    foreach ($Filter in $csv.Name) {
        #and this does the checking and outputs anything that is matched
        If ($folder.name -match $Filter) { "$filter" }
    }
}

答案 1 :(得分:0)

通常,CSV是二维数据结构,因此您无法直接使用它们进行过滤。但是,您可以将二维数组转换为一维数组:

$filter = Import-Csv 'C:\path\to\some.csv' | % {
            $_.PSObject.Properties | % { $_.Value }
          }

如果CSV只有一列,则可以将“mangling”简化为此(将Name替换为实际列名称):

$filter = Import-Csv 'C:\path\to\some.csv' | % { $_.Name }

或者这个:

$filter = Import-Csv 'C:\path\to\some.csv' | select -Expand Name

当然,如果CSV只有一个列,那么最好立即将其设为一个平面列表,因此它可以像这样导入:

$filter = Get-Content 'C:\path\to\some.txt'

无论哪种方式,准备好$filter,您都可以将其应用于输入数据,如下所示:

SearchSubFolders | ? { $filter -contains $_.Name }  # ARRAY -contains VALUE

-match运算符不起作用,因为它将值(左操作数)与正则表达式(右操作数)进行比较。

有关详细信息,请参阅Get-Help about_Comparison_Operators

答案 2 :(得分:0)

另一种选择是从文件名集合创建一个正则表达式,并使用它来一次过滤所有文件名:

$filenames = import-csv filename.csv |
 foreach { $_.name }

[regex]$filename_regex = ‘(?i)^(‘ + (($filenames | foreach {[regex]::escape($_)}) –join “|”) + ‘)$’

$SearchSubfolders | 
 where { $_.name -match $filename_regex }

答案 3 :(得分:0)

如果要将文件的实际名称与列表中的名称相匹配,则可以使用Compare-Object来轻松完成此操作。一个例子:

$filter = import-csv files.csv
ls | Compare-Object -ReferenceObject $filter -IncludeEqual -ExcludeDifferent -Property Name

这将打印当前目录中与Name中的任何files.csv匹配的文件。您也可以通过删除-IncludeEqual-ExcludeDifferent标记来仅打印不同的标记。如果你需要完整的正则表达式匹配,你将需要遍历csv中的每个正则表达式并查看它是否匹配。

这是使用正则表达式过滤器的任何替代解决方案。请注意,我们将创建和缓存正则表达式实例,因此我们不必依赖运行时的内部缓存(默认为15个项目)。首先,我们有一个有用的辅助函数Test-Any,它将循环遍历一个项目数组,并在其中任何一个满足条件时停止:

function Test-Any() {
    param(
    [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
    [object[]]$Items, 
    [Parameter(Mandatory=$True,Position=2)]
    [ScriptBlock]$Predicate)

    begin { 
        $any = $false 
    }
    process {
        foreach($item in $items) {
            if ($predicate.Invoke($item)) {
                $any = $true
                break
            }   
        }   
    }
    end { $any }
}

有了这个,实现相对简单:

$filters = import-csv files.csv | foreach { [regex]$_.Name }
ls -recurse | where { $name = $_.Name; $filters | Test-Any { $_.IsMatch($name) } }

答案 4 :(得分:0)

我最终使用了'循环中的'循环'构造,在经过多次试验和错误后完成了这项工作:

#the SearchSubFolders function was amended to force results in a variable, SearchResults

$SearchResults2 = @()
foreach ($result in $SearchResults){
  foreach ($line in $filter){
    if ($result -match $line){
      $SearchResults2 += $result
    }
  }
}

将CSV文件折叠到基于文本的数组后,这种方法很有用,该数组只包含该CSV中必要的列数据。非常感谢Ansgar Wiechers协助我完成这件事!

所有人都提出了可行的解决方案,有些比我更关心,但如果我能将多个答案标记为正确,我会!我选择的正确答案不仅基于正确性而且还基于简单性......