如何在PowerShell中将foreach更改为for?

时间:2019-03-08 08:53:41

标签: powershell

我要打印单词存在于文本文件中并打印“匹配”和“不匹配”。我的第一个文本文件是:xxaavv6J,我的第二个文件是6J6SCa.yB

如果匹配,则返回如下:

Match found: 
Match found: 
Match found: 
Match found: 
Match found: 
Match found: 6J
Match found: 
Match found: 
Match found: 

我的期望只是打印matchnot match

$X = Get-Content "C:\Users\2.txt"
$Data = Get-Content "C:\Users\d.txt"
$Split = $Data -split '(..)'

$Y = $X.Substring(0, 6)

$Z = $Y -split '(..)'

foreach ($i in $Z) {
    foreach ($j in $Split) {
        if ($i -like $j) { 
            Write-Host ("Match found: {0}" -f $i, $j)
        }
    }
}

3 个答案:

答案 0 :(得分:2)

操作-split '(..)'不会产生您认为会产生的结果。如果查看以下命令的输出,您会看到很多空结果:

PS C:\> 'xxaavv6J' -split '(..)' | % { "-$_-" }
--
-xx-
--
-aa-
--
-vv-
--
-6J-
--

这些空值是您从$i -like $j获得的其他匹配项。

我不太确定为什么-split '(..)'会首先为您提供任何非空值,因为我希望它会为输入字符串“ xxaavv6J”生成5个空字符串。显然,它与分组括号有关,因为-split '..'(不带分组括号)实际上的行为确实符合预期。看起来与捕获组一样,捕获的匹配项将返回到拆分操作结果的顶部。

无论如何,要获得想要替换的行为

... -split '(..)'

使用

... |
    Select-String '..' -AllMatches |
    Select-Object -Expand Matches |
    Select-Object -Expand Value

您还可以将嵌套循环替换为以下内容:

foreach ($i in $Z) {
    if (if $Split -contains $i) {
        Write-Host "Match found: ${i}"
    }
}

答案 1 :(得分:0)

使用正则表达式'.Match()'的方法稍有不同,也应该这样做。
我为您添加了很多解释性注释:

$Test = Get-Content "C:\Users\2.txt" -Raw         # Read as single string. Contains "xxaavv6J"
$Data = (Get-Content "C:\Users\d.txt") -join ''   # Read as array and join the lines with an empty string. 
                                                  # This will remove Newlines. Contains "6J6SCa.yB"

# Split the data and make sure every substring has two characters
# In each substring, the regex special characters need to be Escaped.
# When this is done, we join the substrings together using the pipe symbol.
$Data = ($Data -split '(.{2})' |                                   # split on every two characters
        Where-Object { $_.Length -eq 2 } |                         # don't care about any left over character
        ForEach-Object { [Regex]::Escape($_) } ) -join '|'         # join with the '|' which is an OR in regular expression

# $Data is now a string to use with regular expression: "6J|6S|Ca|\.y"

# Using '.Match()' works Case-Sensitive. To have it compare Case-Insensitive, we do this:
$Data = '(?i)' + $Data

# See if we can find one or more matches
$regex = [regex]$Data
$match = $regex.Match($Test)
# If we have found at least one match:
if ($match.Groups.Count) {
    while ($match.Success) {
        # matched text: $match.Value
        # match start:  $match.Index
        # match length: $match.Length
        Write-Host ("Match found: {0}" -f $match.Value)
        $match = $match.NextMatch()
    }
}
else {
    Write-Host "Not Found"
}

结果:

  

Match found: 6J

答案 2 :(得分:0)

进一步到excellent Ansgar Wiechers' answer:如果您正在运行Windows上的 Windows PowerShell 4.0 ,则可以应用 Kirk Munro .Where()方法>的详尽文章ForEach and Where magic methods

  

随着Windows PowerShell 4.0的发布,两个新的“魔术”方法   引入了为提供新语法的集合类型   访问Windows PowerShell中的ForEachWhere功能。   这些方法恰当地命名为 ForEach Where 。我打电话   这些方法“神奇”,因为它们在工作方式上非常神奇   在PowerShell中。它们不会显示在Get-Member输出中,即使您   申请-Force并请求-MemberType All如果您汇总   袖子,用反射挖出来,你可以找到它们;但是,它   需要广泛的搜索,因为它们是私有扩展方法   在私有类上实现。即使他们不是   可被发现而无需在底下偷看,当您   需要它们,它们比同龄人更快,而且   包括较老版本不可用的功能   同行,因此当您   在PowerShell中使用它们。不幸的是,这些方法仍然存在   直到今天,也就是自公开发布以来,已经有将近一年的时间了   发布,所以很多人没有意识到   这些方法。
  …

     

Where方法

     

Where是一种允许您过滤对象集合的方法。   这非常类似于Where-Object cmdlet,但是Where   方法也类似于Select-ObjectGroup-Object,   包含Where-Object cmdlet的其他一些功能   本身并没有本地支持。此方法提供了更快的速度   比简单Where-Object的命令更优雅。喜欢   ForEach方法,此方法输出的任何对象都是   返回类型的通用集合   System.Collections.ObjectModel.Collection1[psobject]

     

此方法只有一个版本,可以描述为   如下:

Where(scriptblock expression[, WhereOperatorSelectionMode mode[, int numberToReturn]])
     

如方括号所示,expression脚本块为   必需和mode枚举以及numberToReturn整数   参数是可选的,因此您可以使用1、2或3调用此方法   论点。如果要使用特定参数,则必须提供   该参数左侧的所有参数(即,如果您想   为numberToReturn提供一个值,您必须为   modeexpression

适用于您的情况(使用Where(scriptblock expression)方法的最简单的变体.Where()):

$X    = '6J6SCa.yB'  # Get-Content "C:\Users\2.txt"
$Data = 'xxaavv6J'   # Get-Content "C:\Users\d.txt"

$Split = ($Data -split '(..)').Where({$_ -ne ''})

$Y = $X.Substring(0, 6)
$Z = ($Y -split '(..)').Where{$_ -ne ''}    # without parentheses

例如,Ansgar的示例更改如下:

PS > ('xxaavv6J' -split '(..)').Where{$_ -ne ''} | % { "-$_-" }
-xx-
-aa-
-vv-
-6J-