Add the parameter '-Not' to 'Where-Object' (#6464)

This commit is contained in:
Simon Wåhlin 2018-03-27 01:34:13 +02:00 committed by Dongbo Wang
parent 701b919345
commit 31405f7283
2 changed files with 155 additions and 8 deletions

View file

@ -813,6 +813,7 @@ namespace Microsoft.PowerShell.Commands
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "CaseSensitiveNotInSet")]
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "IsSet")]
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "IsNotSet")]
[Parameter(Mandatory = true, Position = 0, ParameterSetName = "Not")]
[ValidateNotNullOrEmpty]
public string Property
{
@ -1199,6 +1200,16 @@ namespace Microsoft.PowerShell.Commands
get { return _binaryOperator == TokenKind.IsNot; }
}
/// <summary>
/// Binary operator -Not.
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = "Not")]
public SwitchParameter Not
{
set { _binaryOperator = TokenKind.Not; }
get { return _binaryOperator == TokenKind.Not; }
}
#endregion binary operator parameters
private readonly CallSite<Func<CallSite, object, bool>> _toBoolSite =
@ -1211,6 +1222,16 @@ namespace Microsoft.PowerShell.Commands
return (x, y) => site.Target.Invoke(site, x, y);
}
private static Func<object, object, object> GetCallSiteDelegateBoolean(ExpressionType expressionType, bool ignoreCase)
{
// flip 'lval' and 'rval' in the scenario '... | Where-Object property' so as to make it
// equivalent to '... | Where-Object {$true -eq property}'. Because we want the property to
// be compared under the bool context. So that '"string" | Where-Object Length' would behave
// just like '"string" | Where-Object {$_.Length}'.
var site = CallSite<Func<CallSite, object, object, object>>.Create(binder: PSBinaryOperationBinder.Get(expressionType, ignoreCase));
return (x, y) => site.Target.Invoke(site, y, x);
}
private static Tuple<CallSite<Func<CallSite, object, IEnumerator>>, CallSite<Func<CallSite, object, object, object>>> GetContainsCallSites(bool ignoreCase)
{
var enumerableSite = CallSite<Func<CallSite, object, IEnumerator>>.Create(PSEnumerableBinder.Get());
@ -1261,12 +1282,7 @@ namespace Microsoft.PowerShell.Commands
}
else
{
// flip 'lval' and 'rval' in the scenario '... | Where-Object property' so as to make it
// equivalent to '... | Where-Object {$true -eq property}'. Because we want the property to
// be compared under the bool context. So that '"string" | Where-Object Length' would behave
// just like '"string" | Where-Object {$_.Length}'.
var site = CallSite<Func<CallSite, object, object, object>>.Create(PSBinaryOperationBinder.Get(ExpressionType.Equal, true));
_operationDelegate = (x, y) => site.Target.Invoke(site, y, x);
_operationDelegate = GetCallSiteDelegateBoolean(ExpressionType.Equal, ignoreCase: true);
}
break;
case TokenKind.Ceq:
@ -1338,6 +1354,9 @@ namespace Microsoft.PowerShell.Commands
_operationDelegate =
(lval, rval) => ParserOps.MatchOperator(Context, PositionUtilities.EmptyExtent, lval, rval, notMatch: true, ignoreCase: false);
break;
case TokenKind.Not:
_operationDelegate = GetCallSiteDelegateBoolean(ExpressionType.NotEqual, ignoreCase: true);
break;
// the second to last parameter in ContainsOperator has flipped semantics compared to others.
// "true" means "contains" while "false" means "notcontains"
case TokenKind.Icontains:
@ -1463,7 +1482,7 @@ namespace Microsoft.PowerShell.Commands
else
{
// Both -Property and -Value need to be specified if the user specifies the binary operation
if (_valueNotSpecified && (_binaryOperator != TokenKind.Ieq || !_forceBooleanEvaluation))
if (_valueNotSpecified && ((_binaryOperator != TokenKind.Ieq && _binaryOperator != TokenKind.Not) || !_forceBooleanEvaluation))
{
// The binary operation is specified explicitly by the user and the -Value parameter is
// not specified
@ -1829,4 +1848,4 @@ namespace Microsoft.PowerShell.Commands
#endregion Set-StrictMode
#endregion Built-in cmdlets that are used by or require direct access to the engine.
}
}

View file

@ -0,0 +1,128 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe "Where-Object" -Tags "CI" {
BeforeAll {
$Computers = @(
[PSCustomObject]@{
ComputerName = "SPC-1234"
IPAddress = "192.168.0.1"
NumberOfCores = 1
Drives = 'C','D'
},
[PSCustomObject]@{
ComputerName = "BGP-5678"
IPAddress = ""
NumberOfCores = 2
Drives = 'C','D','E'
},
[PSCustomObject]@{
ComputerName = "MGC-9101"
NumberOfCores = 3
Drives = 'C'
}
)
}
It "Where-Object -Not Prop" {
$Result = $Computers | Where-Object -Not 'IPAddress'
$Result.Count | Should -Be 2
}
It 'Where-Object -FilterScript {$true -ne $_.Prop}' {
$Result = $Computers | Where-Object -FilterScript {$true -ne $_.IPAddress}
$Result.Count | Should -Be 2
}
It "Where-Object Prop" {
$Result = $Computers | Where-Object 'IPAddress'
$Result.Count | Should -Be 1
}
It 'Where-Object -FilterScript {$true -eq $_.Prop}' {
$Result = $Computers | Where-Object -FilterScript {$true -eq $_.IPAddress}
$Result.Count | Should -Be 1
}
It 'Where-Object -FilterScript {$_.Prop -contains Value}' {
$Result = $Computers | Where-Object -FilterScript {$_.Drives -contains 'D'}
$Result.Count | Should -Be 2
}
It 'Where-Object Prop -contains Value' {
$Result = $Computers | Where-Object Drives -contains 'D'
$Result.Count | Should -Be 2
}
It 'Where-Object -FilterScript {$_.Prop -in $Array}' {
$Array = 'SPC-1234','BGP-5678'
$Result = $Computers | Where-Object -FilterScript {$_.ComputerName -in $Array}
$Result.Count | Should -Be 2
}
It 'Where-Object $Array -in Prop' {
$Array = 'SPC-1234','BGP-5678'
$Result = $Computers | Where-Object ComputerName -in $Array
$Result.Count | Should -Be 2
}
It 'Where-Object -FilterScript {$_.Prop -ge 2}' {
$Result = $Computers | Where-Object -FilterScript {$_.NumberOfCores -ge 2}
$Result.Count | Should -Be 2
}
It 'Where-Object Prop -ge 2' {
$Result = $Computers | Where-Object NumberOfCores -ge 2
$Result.Count | Should -Be 2
}
It 'Where-Object -FilterScript {$_.Prop -gt 2}' {
$Result = $Computers | Where-Object -FilterScript {$_.NumberOfCores -gt 2}
$Result.Count | Should -Be 1
}
It 'Where-Object Prop -gt 2' {
$Result = $Computers | Where-Object NumberOfCores -gt 2
$Result.Count | Should -Be 1
}
It 'Where-Object -FilterScript {$_.Prop -le 2}' {
$Result = $Computers | Where-Object -FilterScript {$_.NumberOfCores -le 2}
$Result.Count | Should -Be 2
}
It 'Where-Object Prop -le 2' {
$Result = $Computers | Where-Object NumberOfCores -le 2
$Result.Count | Should -Be 2
}
It 'Where-Object -FilterScript {$_.Prop -lt 2}' {
$Result = $Computers | Where-Object -FilterScript {$_.NumberOfCores -lt 2}
$Result.Count | Should -Be 1
}
It 'Where-Object Prop -lt 2' {
$Result = $Computers | Where-Object NumberOfCores -lt 2
$Result.Count | Should -Be 1
}
It 'Where-Object -FilterScript {$_.Prop -Like Value}' {
$Result = $Computers | Where-Object -FilterScript {$_.ComputerName -like 'MGC-9101'}
$Result.Count | Should -Be 1
}
It 'Where-Object Prop -like Value' {
$Result = $Computers | Where-Object ComputerName -like 'MGC-9101'
$Result.Count | Should -Be 1
}
It 'Where-Object -FilterScript {$_.Prop -Match Pattern}' {
$Result = $Computers | Where-Object -FilterScript {$_.ComputerName -match '^MGC.+'}
$Result.Count | Should -Be 1
}
It 'Where-Object Prop -like Value' {
$Result = $Computers | Where-Object ComputerName -match '^MGC.+'
$Result.Count | Should -Be 1
}
}