Search multiple variables with Where-Object [duplicate] - powershell

function processSearch {
Get-Process -Name "$processSearch*"
}
function processKill {
Stop-Process -Name "$processSearch*"
}
$processSearch = Read-Host -Prompt "Enter the full or partial name of the process: "
processSearch
if ((Get-Process -Name "$processSearch*") -eq $null) {
Write-Output "ERROR: NO PROCESS FOUND."
[Threading.Thread]::Sleep(3000)
}
if ((Get-Process -Name "$processSearch*") -ne $null) {
$userInput= Read-Host -Prompt "Kill process?"
}
if ($userInput -eq "y" -or "Y") {
processKill
}
elseif ($userInput -eq "n" -or "N") {
Write-Output "Process not killed."
}
else {
Write-Output "ERROR: UNHANDLED INPUT."
}
When my script gets to $userInput= Read-Host -Prompt "Kill process?", and I enter any text, the script will terminate the selected process.
I'm new to scripting, so please let me know where my logic is flawed, thank you.

The issue you're experiencing is because when you're checking the $userInput variable you've forgotten to declare what variable to check against in your -or part of the clause.
This code:
if ($userInput -eq "y" -or "Y") {
processKill
}
elseif ($userInput -eq "n" -or "N") {
Write-Output "Process not killed."
}
else {
Write-Output "ERROR: UNHANDLED INPUT."
}
should become:
if ($userInput -eq "y" -or $userInput -eq "Y") {
processKill
}
elseif ($userInput -eq "n" -or $userInput -eq "N") {
Write-Output "Process not killed."
}
else {
Write-Output "ERROR: UNHANDLED INPUT."
}
You could also make the if statements a little less verbose by using the case-sensitive -cin operator as this will check if the value is in an array of given values as below:
if ($userInput -cin "y", "Y") {
processKill
}
elseif ($userInput -cin "n", "N") {
Write-Output "Process not killed."
}
else {
Write-Output "ERROR: UNHANDLED INPUT."
}

NiMux's answer makes good points, but it's worth taking a step back:
PowerShell's operators are case-insensitive by default:
'y' -eq 'Y' is $true.
To perform case-sensitive operations, prefix the operator name with c; e.g. -ceq:
'Y' -ceq 'Y' is $true, but 'y' -ceq 'Y' is $false.
Therefore:
$userInput -eq 'y' and $userInput -eq 'n' are sufficient in your case.
In case you truly have multiple values to test against, use the -inoperator; e.g.:
$userInput -in 'y', 'n', 'c'
As for what you tried:
Due to PowerShell's operator precedence,
$userInput -eq "y" -or "Y"
is evaluated as:
($userInput -eq "y") -or ("Y")
That is, the RHS operand of the -or operation is string "Y" alone, and evaluating a non-empty string in PowerShell in a Boolean context always yields $true (irrespective of what the string contains).
In effect, your attempt was therefore equivalent to the following, which is always $true:
($userInput -eq "y") -or $true
The immediate (but suboptimal) fix to your attempt would have been what's shown in NiMux's answer: use of two separate -eq operations ($userInput -eq 'y' -or $userInput -eq 'Y').

Related

Checking if two variables either both not null or both null

I have been developing a script to send mail based on variables. I have a script like below.
Each of these 2 variables may be $null or not $null. What's the best practice to check for such condition?
Here is my script:
$variableA = ""
$variableB = ""
if($variableA) {
Write-Host "mail send variableA"
} else {
Write-Host "mail not send variableA"
}
if($variableB) {
Write-Host "mail send variableB"
} else {
Write-Host "mail not send variableB"
}
You can use negated -xor operator:
# Either both nulls or both have values
(-not (($null -eq $a) -xor ($null -eq $b)))
One can disagree what's more readable. I'd personally just go with more explicit formula:
(($null -eq $a) -and ($null -eq $b)) -or
(($null -ne $a) -and ($null -ne $b))
Remember to put $null in the left side of the comparison, it's considered as a best practice.

Trying to return only ipv4 address from hostname

So my code looks like this
Write-Host "TECH ID's `n Ador: 1`n Len: 2 `n Colleen: 3 `n Angel: 4 `n Simon: 5 `n Brian: 6`n Jennifer : 7 `n Tina 8 `n"
$tech_name = Read-Host -Prompt "Please enter Tech ID or IP"
Write-Host "Please Wait, Do not click or type anything."
& "M:\Forms\1 FS Remote\FS_remoteassistance.exe"
Sleep 3
$ping = New-Object System.Net.NetworkInformation.Ping
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate("C:\Windows\system32\cmd.exe")
Sleep 2
if ($tech_name -eq "brian" -or $tech_name -eq "6" ){
$wshell.SendKeys($($ping.Send("xxxxx").Address).IPAddressToString)
$wshell.SendKeys("~")
}
Elseif ($tech_name -eq "angel" -or $tech_name -eq "4"){
$wshell.SendKeys($($ping.Send("xxxxx").Address).IPAddressToString)
$wshell.SendKeys("~")
}
Elseif ($tech_name -eq "len" -or $tech_name -eq "2"){
$wshell.SendKeys($($ping.Send("xxxxx").Address).IPAddressToString)
$wshell.SendKeys("~")
}
Elseif ($tech_name -eq "colleen" -or $tech_name -eq "3"){
$wshell.SendKeys($($ping.Send("xxxxx").Address).IPAddressToString)
$wshell.SendKeys("~")
}
Elseif ($tech_name -eq "ador" -or $tech_name -eq "1"){
$wshell.SendKeys($($ping.Send("xxxxx").Address).IPAddressToString)
$wshell.SendKeys("~")
}
Elseif ($tech_name -eq "simon" -or $tech_name -eq "5"){
$wshell.SendKeys([System.Net.Dns]::GetHostAddresses("")[0])
$wshell.SendKeys("~")
}
Elseif ($tech_name -eq "7"){
$wshell.SendKeys($($ping.Send("xxxxx").Address).IPAddressToString)
$wshell.SendKeys("~")
}
Elseif ($tech_name -eq "8"){
$wshell.SendKeys($($ping.Send("xxxxxx").Address).IPAddressToString)
$wshell.SendKeys("~")
}
Else{
$wshell.SendKeys($tech_name)
$wshell.SendKeys("~")
}
So the goal of this script is to take a hostname and return the ipv4 address so that clients can run the program and connect to technicians without knowing their ip address. The problem is I need this to only send the ipv4 address, but on some computers its sending the ipv6 and on others its sending ipv4. Any thoughts?
i don't know why you are using two different calls to get the IPs, but this will return the IPv4 for the named system ...
[System.Net.Dns]::GetHostAddresses($env:COMPUTERNAME).
Where({$_.AddressFamily -eq 'InterNetwork'}).
IPAddressToString
replace the $env:COMPUTERNAME with your target system name and that otta give you what you need. [grin]
the above requires ps4+ to use the .Where() array method. here's one that uses the pipeline & the Where-Object technique. it SHOULD work on ps2, but i cannot test that.
([System.Net.Dns]::GetHostAddresses($env:COMPUTERNAME) |
Where-Object {$_.AddressFamily -eq 'InterNetwork'}).
IPAddressToString

Powershell bitlocker check

Hi im having diffeculties to get my script working:
It keeps failing on the first write output, even when the powershell version is higher then 4. It only works when I remove And $winver -eq $os1 -or $os2 -or $os3.
Otherwise it keeps telling me my powershell version needs to be upgraded. Im on V5 currently and $PSVersionTable.PSVersion.Major does says it 5 indeed.
What am i doing wrong?
$winver = (Get-WmiObject -class Win32_OperatingSystem).Caption
$powershellversion = $PSVersionTable.PSVersion.Major
$os1 = "Microsoft Windows 7 Professional"
$os2 = "Microsoft Windows 10 Pro"
$os3 = "Microsoft Windows 10 Enterprise"
if($winver -ne ($os1, $os2, $os3) -contains $winver){
Write-Host "Bitlocker not supported on $winver"
Exit 0
}
if($powershellversion -lt 4){
Write-Host "Upgrade Powershell Version"
Exit 1010
}
else
{
$bitlockerkey = (Get-BitLockerVolume -MountPoint C).KeyProtector.RecoveryPassword
$pcsystemtype = (Get-WmiObject -Class Win32_ComputerSystem).PCSystemType
if ($pcsystemtype -eq "2"){
$setsystemtype = "Laptop"
}
else {
$setsystemtype = "Desktop"
}
if ($setsystemtype -eq "laptop" -And $bitlockerkey -eq $null -and ($os1, $os2, $os3) -contains $winver){
Write-Host "$setsystemtype without bitlocker"
Exit 1010
}
if ($setsystemtype -eq "desktop" -And $bitlockerkey -eq $null -and ($os1, $os2, $os3) -contains $winver){
Write-Host "$setsystemtype without bitlocker"
Exit 0
}
if ($winver -eq ($os1, $os2, $os3) -contains $winver){
Write-Host "$bitlockerkey"
Exit 0
}
}
Let's see what this actually does:
if ($powershellversion -lt 4 -And $winver -eq $os1 -or $os2 -or $os3) { ... }
If your powershell version is less than 4, and Win version is equal
to os1, then proceed
If os2 has a value, then proceed
If os3 has a value, then proceed
The topic here is Operator Precedence, specifically, what happens first when evaluating a line of code, what happens second, third, and so on. Just like in algebra math, adding parens around part of the formula changes the order in whcih you read it.
So, you can mess around with parens to get your logic to work:
if($powershellversion -lt 4 -and ( ($winver -eq $os1) -or ($winver -eq $os2) -or ($winver -eq $os3) ))
In other words
evaluate if PS version is < 4 ($powershellversion -lt 4), -and
evaluate if winver is either os1, os2 or os3: ( ($winver -eq $os1) -or ($winver -eq $os2) -or ($winver -eq $os3) ).
Or, you can rearrange your logic a bit by putting your os variables into an array, and seeing if $winver is in there:
if($powershellversion -lt 4 -and $winver -in ($os1, $os2, $os3)) { ... }
Edit: Or
if($powershellversion -lt 4 -and ($os1, $os2, $os3) -contains $winver) { ... }
for backwards compatibility with v2.0.

Powershell - foreach loop is exiting prematurely?

Forgive me, but I'm very new to Powershell. I'm having a problem understanding why my code is abruptly exiting the foreach loop once the code finds the book with an ID called "bk103". I'm expecting that the foreach loop iterate through the for loop 12 times since there are 12 books in the xml file. Why is it exiting early?
Thank you!
Set-Location 'C:\PowershellPractice\' #'
[int]$index = 0
[string] $xmlFilePath=’C:\PowershellPractice\books.xml’
[xml] $xmlContent = [xml] (Get-Content -Path $xmlFilePath)
$newSubNode = $xmlContent.CreateElement("security")
$newSubNode.SetAttribute("Mode", "transport")
$collection = $xmlContent.catalog.ChildNodes
Write-Host "Collection has " $collection.Count " elements in it!"
foreach ($item in $collection){
Write-Host "In foreach, index is " $index
if ($item.id -eq "bk103" -OR
$item.id -eq "bk105" -OR
$item.id -eq "bk108" -OR
$item.id -eq "bk109"){
Write-Host "Found book called " $ $item.id
$elementCopy = $xmlContent.catalog.book[$index].Clone()
$elementCopy.AppendChild($newSubNode)
$xmlContent.catalog.RemoveChild($item)
$xmlContent.catalog.InsertBefore($elementCopy,$xmlContent.catalog.book[$index])
}
$index++
}
$xmlContent.Save('C:\PowershellPractice\books-edited.xml')
As mentioned in the comments, you cannot modify the collection while you're currently iterating over it.
Since you only really need to modify the elements in the collections themselves, I would recommend just doing that (instead of cloning the book node and re-attaching it):
foreach ($item in $collection){
Write-Host "In foreach, index is " $index
if ($item.id -eq "bk103" -OR
$item.id -eq "bk105" -OR
$item.id -eq "bk108" -OR
$item.id -eq "bk109"){
# Add subnode to matching $item
[void]$item.AppendChild($newSubNode)
}
}
If you ever find yourself in a situation where you cannot modify the element in place, use two loops - one to find the interesting elements, and another one to replace them by iterating over the resulting subset:
# collect matching results
$foundbooks = foreach ($item in $collection){
Write-Host "In foreach, index is " $index
if ($item.id -eq "bk103" -OR
$item.id -eq "bk105" -OR
$item.id -eq "bk108" -OR
$item.id -eq "bk109"){
# return matching $item
$item
}
}
# now modify based on initial results
foreach($book in $foundbooks){
Write-Host "Found book called " $ $book.id
$elementCopy = $book.Clone()
[void]$elementCopy.AppendChild($newSubNode)
[void]$xmlContent.catalog.InsertBefore($elementCopy,$book)
[void]$xmlContent.catalog.RemoveChild($book)
}

-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.