【问题标题】:Powershell Use Wildcards when matching arraysPowershell 匹配数组时使用通配符
【发布时间】:2016-12-12 18:28:41
【问题描述】:

我已经用头撞墙好几个小时了,正在寻求帮助。为了简化我的问题,我有两个数组,一个包含通配符,另一个使用这些通配符:

$WildCardArray = @("RED-*.htm", "*.yellow", "BLUE!.txt", "*.green", "*.purple")
$SpelledOutArray = @("RED-123.htm", "456.yellow", "BLUE!.txt",  "789.green", "purple.102", "orange.abc")

我无法让 PowerShell 识别这些匹配。

我的最终目标是得到一个输出,告诉我purple.102 和orange.abc 不在$WildCardArray 中。

看起来超级简单!我尝试过的一些事情:

$WildCardArray = @("RED-*.htm", "*.yellow", "BLUE!.txt", "*.green", "*.purple")
$SpelledOutArray = @("RED-123.htm", "456.yellow", "BLUE!.txt",  "789.green", "purple.102", "orange.abc")
foreach($Item in $SpelledOutArray)
{
$item | where {$wildcardarray -contains $item}
}

结果我得到了 BLUE!.txt,因为它是我的控件,没有通配符。如果我将其更改为 -notcontains,我将返回除 BLUE 之外的所有结果。我试过包含、匹配、等于、喜欢以及它们的所有对立面、比较对象,但没有任何效果。我没有收到任何错误,只是没有得到预期的结果

我尝试用 [a-zA-Z] 和其他组合替换“*”,但它是按字面意思替换它,而不是作为通配符。我不确定我做错了什么...... PSVersion 5.1 Win 10

有人知道为什么喜欢/匹配/包含不起作用的逻辑,我可以做些什么让它起作用?它不必漂亮,它只需要工作

【问题讨论】:

  • -contains 寻找完全匹配,我很确定它威胁* 作为一个没有任何特殊含义的字符
  • $WildCardArray | ForEach-Object {$Wildcard = $_ ; $SpelledOutArray | Where-Object {$_ -like $WildCard}}
  • @beatcracker 输出错误。

标签: arrays regex powershell wildcard


【解决方案1】:

用头撞墙几个小时 [..] 看起来超级简单!

这可能暗示它不是超级简单。您正在尝试交叉匹配两个列表:红色到红色、黄色、蓝色......然后蓝色到红色、黄色、蓝色......然后绿色到红色、黄色、蓝色...... . 30 次比较,但只有 5 次循环发生。

你需要更多。

$WildCardArray = @("RED-*.htm", "*.yellow", "BLUE!.txt", "*.green", "*.purple")
$SpelledOutArray = @("RED-123.htm", "456.yellow", "BLUE!.txt",  "789.green", "purple.102", "orange.abc")

# Loop once over the spelled out items
foreach($Item in $SpelledOutArray)
{
    # for each one, loop over the entire WildCard array and check for matches
    $WildCardMatches = foreach ($WildCard in $WildCardArray)
    { 
        if ($item -like $WildCard) {
            $Item
        }
    }

    # Now see if there were any wildcard matches for this SpelledOut Item or not
    if (-not $WildCardMatches)
    {
        $Item 
    }
}

WildCardArray 上的内部循环可以成为过滤器,但您必须过滤数组,而不是像您的代码那样过滤单个项目。

$WildCardArray = @("RED-*.htm", "*.yellow", "BLUE!.txt", "*.green", "*.purple")
$SpelledOutArray = @("RED-123.htm", "456.yellow", "BLUE!.txt",  "789.green", "purple.102", "orange.abc")

foreach($Item in $SpelledOutArray)
{
   $WildCardMatches = $wildcardarray | Where { $item -like $_ }

   if (-not $WildCardMatches)
   {
       $Item 
   }
}

而且我猜如果必须的话,你可以把它混入一个不清楚的双位置过滤器。

$WildCardArray = @("RED-*.htm", "*.yellow", "BLUE!.txt", "*.green", "*.purple")
$SpelledOutArray = @("RED-123.htm", "456.yellow", "BLUE!.txt",  "789.green", "purple.102", "orange.abc")

$SpelledOutArray |Where {$item=$_; -not ($WildCardArray |Where {$item -like $_}) }

【讨论】:

  • 这很好用,谢谢!您知道吗,您的解决方案如何在大量数据下保持稳定?例如,如果 $WildCardArray 包含 1000 个项目和 $SpelledOutArray 超过 100k?
  • @Nick 它会工作,但可能很慢,它是 O(N*M) 运行时。如果您可以将所有通配符组合成一个正则表达式,例如,您可能会更好。 @("RED-123.htm", "456.yellow", "BLUE!.txt", "789.green", "purple.102", "orange.abc") -notmatch '^RED-.*\.htm|.*\.yellow|BLUE!\.txt|.*\.green|.*\.purple$' 是为您的示例做的。但是您说您的示例已简化,因此对于您真正在做的事情来说,这可能并不容易/不可能。
  • @TessellatingHeckler:有趣的是你应该提到它;这就是我的回答;-)
  • 通过 invoke-webrequest 从网站获取文件名/扩展名列表,将其存储在数组 (WildCardArray) 中,然后递归查询数千个共享 (SpelledOutArray),可能有数百万个文件可供查看如果任何文件与 webrequest 中的任何内容都匹配一个 foreach 并行工作流程......所以当我说简化时,我的意思是超级简化:(只要它有效,我就不会超级担心时间。
【解决方案2】:

您的通配符数组实际上是要查找的模式列表。您可以将其转换为单个正则表达式并与之匹配:

$WildCardArray = @("RED-*.htm", "*.yellow", "BLUE!.txt", "*.green", "*.purple")
$SpelledOutArray = @("RED-123.htm", "456.yellow", "BLUE!.txt",  "789.green", "purple.102", "orange.abc")

# Turn wildcards into regexes
# First escape all characters that might cause trouble in regexes (leaving out those we care about)
$escaped = $WildcardArray -replace '[ #$()+.[\\^{]','\$&' # list taken from Regex.Escape
# replace wildcards with their regex equivalents
$regexes = $escaped -replace '\*','.*' -replace '\?','.'
# combine them into one regex
$singleRegex = ($regexes | %{ '^' + $_ + '$' }) -join '|'

# match against that regex
$SpelledOutArray -notmatch $singleRegex

这有可能比循环检查所有内容更快,尽管我没有测试。此外,过长的正则表达式也可能会造成麻烦。

【讨论】:

  • 我一直在搞乱这个解决方案并且有一个问题。它似乎没有从字面上理解“。”,这意味着 *.yellow 的通配符将拉“123.greenyellow”。这是正则表达式的怪癖吗?我查了通配符,但似乎无法弄清楚。
  • @Nick:抱歉,我的转义替换实际上删除了所有这些字符,而不是转义它们。我现在修好了。
  • 太棒了!感谢这个模式。 提示: 这是一种简单的模式,可以包装在函数中以便于重用。如果您有 $ArrayToCheck -notmatch ToSingleRegex($ComparisonStringArray) 的库或示例实现,请随时在此处发表评论
【解决方案3】:
$WildCardArray = @("RED-*.htm", "*.yellow", "BLUE!.txt", "*.green", "*.purple")
$SpelledOutArray = @("RED-123.htm", "456.yellow", "BLUE!.txt",  "789.green", "purple.102", "orange.abc")

$WildCardArray | %{$str=$_; $SpelledOutArray | ? {$_ -like $str}  }

其他解决方案,不短

$WildCardArray | 
   %{$current=$_; $SpelledOutArray | %{ [pscustomobject]@{wildcard=$current; value=$_ }}} | 
        where {$_.value -like $_.wildcard } 

【讨论】:

  • 这给出了错误的输出...“我的最终目标是有一个输出告诉我 Purple.102 和 orange.abc 不在 $WildCardArray 中。”但您的代码输出RED-*.htm *.yellow BLUE!.txt *.green
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-29
  • 1970-01-01
  • 2015-09-30
  • 2021-09-25
  • 2012-03-26
  • 2017-05-01
相关资源
最近更新 更多