在PowerShell中被-match和-notmatch RegEx困扰的结果

时间:2019-08-05 14:55:07

标签: powershell

我有一些代码旨在为不在驱动器号列表中的每个磁盘驱动器返回PSDriveInfo对象。我可以求逆,也可以对它进行编码,但是我真的很想了解我要去哪里。我的服务器有7个硬盘驱动器(C,D,E,H,I,L,W),我的代码旨在为除驱动器E和H之外的所有驱动器返回PSDriveInfo对象。

我尝试了各种比较运算符-ne.ToString().Trim().Equals()-match。我的代码当前基于RegEx -match运算符。如果我使用-match,我得到的就是我想要的东西,但是如果我使用-notmatch,我得到了每个驱动器,我感到困惑,为什么这不起作用。在代码窗口中,我首先显示有效的方法,然后给出与我想要的相反的代码,然后使使用-notmatch运算符的代码使我感到困惑。

Clear-Host
$exclude = "E,H"

"This gives me the inverse of what I want:"
Get-WmiObject Win32_LogicalDisk | Where-Object {
    $_.DriveType -eq 3
} | ForEach-Object {
    Get-PSDrive $_.DeviceId[0]
} | Where-Object {
    $driveName = $_.Name
    $exclude -split "," | ForEach-Object {
        if ($driveName -match $_) { return $true }
    }
} | ForEach-Object {
    $_.Name
}

"Why does this not work:"
Get-WmiObject Win32_LogicalDisk | Where-Object {
    $_.DriveType -eq 3
} | ForEach-Object {
    Get-PSDrive $_.DeviceId[0]
} | Where-Object {
    $driveName = $_.Name
    $exclude -split "," | ForEach-Object {
        if ($driveName -notmatch $_) { return $true }
    }
} | ForEach-Object {
    $_.Name
}

服务器上的代码示例的结果:

这与我使用-match的含义相反:

E
H

为什么-notmatch不起作用?

C
D
E
H
I
L
W

3 个答案:

答案 0 :(得分:3)

在您的-notmatch尝试中,您正在检查最后Where-Object内排除类别中的每个项目与类型3的每个驱动器。因此,您将始终获得至少一个$true评估。因此,这意味着where-object条件将始终等于$true

这是一个琐碎的事,尽管仍然很令人费解,它是正在发生的事的例子:

# Example 1
@(1,2,3,4) | where-object { $num = $_; $true,$false,$true |% {if ($_) {$num}}}
1
2
3
4

#Example 2:
@(1,2,3,4) | where-object { $num = $_; $false,$false,$false |% {if ($_) {$num}}}

上面的示例1总是最终求值为$true,以允许输出当前数组对象。示例2从不将$true传递到if语句中,因此它什么也不返回。您的-notmatch代码起作用的唯一方法是,如果没有匹配项,说实话,我不知道何时会发生。

我已采用您的代码并对其进行了修改,以向您显示正在发生的事情。如果运行此命令,则将看到每个驱动器至少返回一个True

$exclude = "E,H"

Get-wmiobject Win32_LogicalDisk | 
    Where-Object { 
        $_.drivetype -eq 3 
    } | 
    ForEach-Object {
        Get-PSDrive $_.deviceid[0] 
    } | 
    foreach-Object { 
        $driveName = $_.Name
        $exclude -split "," | 
        ForEach-Object{ 
            "Testing Drive $driveName -notmatch $_"
            if( $driveName -notmatch $_ ){ $true } 
            else {$false}
        }
    }

使用上述代码进行测试:

Testing Drive H -notmatch E
True
Testing Drive H -notmatch H
False
Testing Drive O -notmatch E
True
Testing Drive O -notmatch H
True
Testing Drive T -notmatch E
True
Testing Drive T -notmatch H
True

解决此问题的一种更简单的方法是使用-notin运算符。

$exclude = "E:","H:"

Get-WmiObject Win32_LogicalDisk | 
    Where-Object {$_.DriveType -eq 3 -and $_.DeviceId -notin $exclude}

在上面的代码中,$exclude已经是一个数组,每个值都包含:,以简化代码。如果您不想在变量中包含:,则可以在下一行中将其连接起来,如下所示:

$exclude = "E","H" | Foreach-Object {"{0}:" -f $_}

Get-WmiObject Win32_LogicalDisk | 
    Where-Object {$_.DriveType -eq 3 -and $_.DeviceId -notin $exclude}

答案 1 :(得分:2)

您的逻辑...令人费解,脆弱,无法正确说明push-match对集合的工作方式。一种更合乎逻辑的方法是使用-notmatch-in集合运算符。

-notin

输出...

$ComputerName = $env:COMPUTERNAME
$ExcludedDriveList = 'e', 'h'

$DriveLetterList = @(Get-CimInstance -ClassName CIM_LogicalDisk |
    Where-Object {
        $_.DriveType -eq 3 -and
        $_.DeviceID.Trim(':') -notin $ExcludedDriveList
        }
    ).DeviceID.Trim(':')

$DriveLetterList

那只是HD / SSD项,我有两个dvd-r驱动器和一个未列出的读卡器。 [咧嘴]

答案 2 :(得分:1)

问题在于"E", "H" -NotMatch "E"将返回“ H”-请参见Microsoft Docs on Comparison Operators。在这种情况下,您想要的只是取消您的if语句:

...
if(!( $driveName -match $_ )) { return $true } # or if (-not ($driveName -match $_ ))
...