Conditional PowerShell operators - powershell

I am trying to get a list where the title does not have temp or temporary or *contractor or contractor*.
This code is working, meaning I get a list which does not have temporary records.
$pTitle = $profile["Title"]
if ($pTitle -ne "Temporary")
However the following code does not work when I add -or and -notlike for the wildcard.
$pTitle = $profile["Title"]
if ($pTitle -ne "Temporary" -or $pTitle -notlike "Temporary" -or $pTitle -notlike "contractor" -or $pTitle -notlike "Temp")

You actually want -and here. Your expression will only evaluate to false if all of the words are found currently.

$List = #(1,2,3)
If I ask you to return all the items in the list that are:
not equal to 1
or
not equal to 2
or
not equal to 3
the answer will be the whole list
$List | where { ($_ -ne 1) -or ($_ -ne 2) -or ($_ -ne 3) }
1
2
3
You are getting your logical operators mixed up.
Look at replacing -or with -and

Related

Nesting ForEach within If statement (PowerShell)

I am trying to loop through a list of Windows services and if any of them match a certain criteria, I would like to return exit code 1.
If there is no match, I would like to return exit code 0.
I am struggling to put this within an If statement, I think I'm putting the code in the wrong place!
Could anyone lend me a hand? Script below.
Thanks in advance.
Adrian
try
{
#Pull list of services from registry
$svclist = Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\services | ForEach-Object {Get-ItemProperty $_.PsPath}
#Ignore anything after .exe, filter for vulnerable services
ForEach ($svc in $svclist) {
$svcpath = $svc.ImagePath -split ".exe"
if(($svcpath[0] -like "* *") -and ($svcpath[0] -notlike '"*') -and ($svcpath[0] -notlike "\*")) {
$svc | fl -Property DisplayName,ImagePath,PsPath
}
}
if (($svc -ne $null)){
Write-Host "Match"
Return $svc.count
exit 1
}
else{
Write-Host "No_Match"
exit 0
}
}
catch{
$errMsg = $_.Exception.Message
Write-Error $errMsg
exit 1
}
You can either return early or use a variable with a single [bool] value to keep track of whether anything was matched:
return early
foreach($svc in $svcList)
{
$svcpath = $svc.ImagePath -split ".exe"
if(($svcpath[0] -like "* *") -and ($svcpath[0] -notlike '"*') -and ($svcpath[0] -notlike "\*")) {
return 1
}
}
# if we've reached this point then no matches occurred
return 0
Using a [bool]
$matchFound = $false
foreach($svc in $svcList)
{
$svcpath = $svc.ImagePath -split ".exe"
if(($svcpath[0] -like "* *") -and ($svcpath[0] -notlike '"*') -and ($svcpath[0] -notlike "\*")) {
$matchFound = $true
}
}
return [int]$matchFound # $false = 0, $true = 1
I always try to use the most powershell-correct methods to achieve what I want. Especially when going through data I prefer to try filtering with the x-object cmdlets.
In your case my suggestion would be to simply loop over the original list with the Where-Object command, this allows you to retrieve a list of items that conform to your search, kind of like an SQL query:
$resultList = $svclist | Where-Object {
($_.ImagePath -like "* *") -and ($_.ImagePath -notlike '"*') -and ($_.ImagePath -notlike "\*")
}
In this case I skipped over the -split ".exe" part, as I didn't quite understand it's purpose, but you could also put that in your filter using regular expressions with the -match operator instead of the -like and -notlike values you make one regex match
Then you can check if that list is populated or not:
if ($resultList) {
return 1
}
else {
return 0
}
It is also considered best practice to only use the aliases for commands (e.g. fl should be Format-List). This will increase readability for future maintenance, of course if it's a one-time script is would be more appropriate. I just try to avoid it as much as I can these days.

Where object as variable

After reading many of the posted solutions here, none fully applies mine.
This works (returns expected number of instancesNames based on criteria):
$response.result | Where-Object {$_.targetType -eq "webserver" -and ($_.agentHostName -eq "ServerA" -or $_.agentHostName -eq "ServerB")} | select-Object "instanceName"
However, since n number of servers may be found, I created a loop to dynamically create this query:
[System.Text.StringBuilder]$clause = " {`$_.targetType -eq ""webserver"" -and ("
$i = 1;
foreach ($server in $serversArray) {
if ( $i -eq $serversArray.Count ) {
$clause.Append("`$_.agentHostName -eq ""${server}"")}")
} else {
$clause.Append( "`$_.agentHostName -eq ""${server}"" -or ")
}
$i++
}
$clause.Append(" | select-Object ""instanceName""")
$filter = [scriptblock]::Create($clause)
$instances = $response.result | where-object $filter
debugging:
the $clause variable contains:
{$_.targetType -eq "webserver" -and ($_.agentHostName -eq "serverA" -or $_.agentHostName -eq "serverB")} | select-Object "instanceName"
However, it returns all instanceNames (not filtered) instead of the ones that meet the criteria. What am I doing wrong here?
The -in operator would simplify your code. For example:
$response.result |
Where-Object {($_.targetType -eq 'webserver') -and ($_.agentHostName -in $serversArray)} |
Select-Object 'instanceName'

Adding multiple -and and -or statements in WHERE clause in Powershell

I'm currently using Powershell, and i'm trying to sort data in my variable that i'm pulling from another system. At the moment i'm trying to use one -and statement and -or statements after an -eq sign. So I was wondering what's the correct way I need to make the syntax in my where statement.
$DLPList | Select Node.NodeName, Properties.OSType, PropsView.version, Node.NodeTextPath2 | where PropsView.version -ne '1.4.706.172' -and (Properties.OSType -eq $win7 -or $win8 -or $win81 -or $win10)
I know there's multiple other stuff on this, but everything i've tried so far doesn't work. Thanks for the help.
Yes, you have to use the script block version of where-object. Also note that -and and -or have EQUAL PRECEDENCE in powershell, which is very unusual in a language.
Alright, what I figured out for this is that you cannot just add use -or. Instead you must do Properties.ostype for each one. Here is the code that I came up with to get it to work. Also a little tip is to do Where-Object before Select-Object to save computing power.
$DLPList = $list1 | Where-Object{ $_.'UDLP.prodversion' -lt '9.5.704.112' -and ($_.'Properties.OSType' -ne 'Windows XP') -and ($_.'EPOComputerProperties.OSType' -ne 'Windows Vista') -and ($_.'EPOComputerProperties.OSType' -ne 'Windows 2003') -and ($_.'Properties.OSType' -ne 'Windows 2003 R2') -and ($_.'EPOComputerProperties.OSType' -ne 'Windows 2008') -and ($_.'Properties.OSType' -ne 'Windows 2008 R2') -and ($_.'Properties.OSType' -ne 'Windows Server 2012' -and ($_.'Properties.OSType' -ne 'Windows Server 2012 R2') -and ($_.'Properties.OSType' -ne 'Windows Server 2016')) } | Select-Object Node.NodeName, Properties.OSType, UDLP.productversion, Node.NodeText | Sort-Object -Property Node.NodeText -Descending
Try adding brackets and replacing the chained boolean operators with a regular expression "-match" operator condition:
$DLPList | Select Node.NodeName, Properties.OSType, PropsView.version, Node.NodeTextPath2 | where { $_.'PropsView.version' -ne '1.4.706.172' -and ($_.'Properties.OSType' -match "($win7|$win8|$win81|$win10)") }
Here's sample code that works for me:
$DLPList = #(2, 4, 6, 7)
$DLPList | where { ($_ -ne 7) -and ($_ -lt 10) -and ($_ -gt 0) }
2
4
6
$DLPList | where { ($_ -ne 7) -and ($_ -match "(4|5|11)" -and ($_ -gt 0)) }
4

Clarification of syntax used

Can someone explain me this syntax? I think it didn't work, so if you can help me to find another syntax.
$fichesAEnvoyer = #($fiches | where {($_.TypeFiche -eq '2') -and (($_.Causes -eq '') -or ($_.Causes -eq $null) -or ($_.Causes.Count -eq 0) -or ($_.ActionCorrective -eq '') -or ($_.ActionCorrective -eq $null) -or ($_.DateActionCorrective -eq '') -or ($_.DateActionCorrective -eq $null) -or ($_.ActionPreventive -eq '') -or ($_.ActionPreventive -eq $null) -or ($_.DateActionPreventive -eq '') -or ($_.DateActionPreventive -eq $null) )})
I don't understand why there is $_ instead of $fiches
$_ is a variable that the where cmdlet creates to represent the current object in the pipeline. For example, if you do:
1,2,3 | where {$_ -ge 2} | Write-Host
Then $_ will be set to 1 followed by 2, followed by 3.

-match several $vars in if statement

I have this, which doesn't work:
$var1 = "6.0.6001"
$var2 = "6.1.7001"
$var3 = "6.2.8074"
$var4 = "6.3.8074"
if($var1 -match "6.1.?" -or "6.2.?" -or "6.3.?") {
write-host "1"
}else{
write-host "2"
}
No matter what, 1 is returned.
What should this really look like?
Thanks.
Run this code:
if("6.2.?" -or "6.3.?") {
write-host "1"
}else{
write-host "2"
}
It'll also return 1 no matter what.
Change your condition to:
if($var1 -match "6.1.?" -or $var1 -match "6.2.?" -or $var1 -match "6.3.?")
Or even better:
if($var1 -match "6.[1-3].?")
Just to elaborate on Adam 's answer. You If statement was not working as intended since PowerShell only saw three conditions you didnt intend
if($var1 -match "6.1.?" -or "6.2.?" -or "6.3.?")
if(($var1 -match "6.1.?") -or ("6.2.?" -or "6.3.?"))
The two lines above function the same. To break down the second operation ("6.2.?" -or "6.3.?") just a little more: A non-zero length string converted to boolean will always be $true. Comparing two non-zero length string with -or will always return $true. The first clause ($var1 -match "6.1.?") in your example is $false. If($true -or $false) is essentially what your If statement boils down to which, again, would always return $true. Adams answer show how to get the logic you are looking for
if($var1 -match "6.1.?" -or $var1 -match "6.2.?" -or $var1 -match "6.3.?")
if(($var1 -match "6.1.?") -or ($var1 -match "6.2.?") -or ($var1 -match "6.3.?"))
Both the above statements are the same. The second one helps understand the logic. For more information see about_Logical_Operators. You only need brackets when the logic is not acting the way you want it to.