The PowerShell -and conditional operator - powershell

Either I do not understand the documentation on MSDN or the documentation is incorrect.
if($user_sam -ne "" -and $user_case -ne "")
{
Write-Host "Waaay! Both vars have values!"
}
else
{
Write-Host "One or both of the vars are empty!"
}
I hope you understand what I am attempting to output. I want to populate $user_sam and $user_case in order to access the first statement!

You can simplify it to
if ($user_sam -and $user_case) {
...
}
because empty strings coerce to $false (and so does $null, for that matter).

Another option:
if( ![string]::IsNullOrEmpty($user_sam) -and ![string]::IsNullOrEmpty($user_case) )
{
...
}

Try like this:
if($user_sam -ne $NULL -and $user_case -ne $NULL)
Empty variables are $null and then different from "" ([string]::empty).

The code that you have shown will do what you want iff those properties equal "" when they are not filled in. If they equal $null when not filled in for example, then they will not equal "". Here is an example to prove the point that what you have will work for "":
$foo = 1
$bar = 1
$foo -eq 1 -and $bar -eq 1
True
$foo -eq 1 -and $bar -eq 2
False

Related

Inconsistent behavior in powershell with null parameters

I need to write a function in powershell that tells apart a 'parameter not being passed' from one passed with string empty (or any other string)
I wrote it like this:
function Set-X {
param(
[AllowNull()][string]$MyParam = [System.Management.Automation.Language.NullString]::Value
)
if ($null -ne $MyParam) { write-host 'oops' }
else { write-host 'ok' }
}
If I call Set-X without parameters from ISE, it works as I expect and prints 'ok'.
But if I do that from the normal console, it prints 'oops'.
What is going on? What is the proper way to do it?
Allowing the user to pass in a parameter argument value of $null does not change the fact that powershell will attempt to convert it to a [string].
Converting a $null value in powershell to a string results in an empty string:
$str = [string]$null
$null -eq $str # False
'' -eq $str # True
(same goes for $null -as [string] and "$null")
Remove the type constraint on the MyParam parameter if you not only want to allow $null but also accept $null as a parameter value:
function Set-X {
param(
[AllowNull()]$MyParam = [System.Management.Automation.Language.NullString]::Value
)
if ($null -ne $MyParam) { write-host 'oops' }
else { write-host 'ok' }
}
As Mathias and BenH have written, the culprit is casting $null to the [string] type, which results in an empty string:
[string]$null -eq '' #This is True
But for the sample code in Mathias answer to work correctly we also have to replace
[System.Management.Automation.Language.NullString]::Value
with $null
function Set-X {
param(
[AllowNull()]$MyParam = $null
)
if ($null -ne $MyParam) { write-host 'oops' }
else { write-host 'ok' }
}

Using -notcontains to find substring within string within array

I'm trying to avoid using nested ForEach Loop as part of a larger code. To do this, I'm using the -notcontains operator. Basically, I want to see if a substring exists within a string within an array. If it exists, do nothing, if it does not exist, print "Not Found".
Here is the code...
$arr = #('"value11","value21","value31"','"value12","value22","value32"','"value13","value23","value33"')
if ($arr -notcontains "*`"value24`"*")
{
Write-Host "Not Found"
}
if ($arr -notcontains "*`"value22`"*")
{
Write-Host "Not Found 2"
}
We can see that value24 is not within any strings of the array. However, value22 is within the 2nd string in the array.
Therefor the results should output the following...
Not Found
However, instead I see the following output...
Not Found
Not Found 2
Can anyone tell me why this is happening?
-contains and -notcontains don't operate against patterns.
Luckily, -match and -like and their negative counterparts, when used with an array on the left side, return an array of the items that satisfy the condition:
'apple','ape','vape' -like '*ape'
Returns:
ape
vape
In an if statement, this still works (a 0 count result will be interpreted as $false):
$arr = #('"value11","value21","value31"','"value12","value22","value32"','"value13","value23","value33"')
if ($arr -notlike "*`"value24`"*")
{
Write-Host "Not Found"
}
My take on a solution:
($arr | foreach {$_.contains('"value24"')}) -contains $true
Using the V3 .foreach() method:
($arr.ForEach({$_.contains('"value24"')}).contains($true))
And yet another possibility:
[bool]($arr.where({$_.contains('"value24"')}))
Edit for clearer answer on what I'm looking for...
This is the only way I'm able to figure this out so far. I hope there is a much cleaner solution...
$arr = #('"value11","value21","value31"','"value12","value22","value32"','"value13","value23","value33"')
$itemNotFound = $true
ForEach ($item in $arr)
{
If ($itemNotFound)
{
If ($item -like "*`"value24`"*")
{
$itemNotFound = $false
}
}
}
if ($itemNotFound)
{
Write-Host "Not Found value24"
}
$itemNotFound = $true
ForEach ($item in $arr)
{
If ($itemNotFound)
{
If ($item -like "*`"value22`"*")
{
$itemNotFound = $false
}
}
}
if ($itemNotFound)
{
Write-Host "Not Found value22"
}
output will be:
Not Found value24

Powershell function argument default: Weirdness when it has a type constraint

If I have a function parameter WITHOUT the type constraint:
> function a ($s=$null) {if ($s -eq $null) {Write-Host "HI"} if ($s -eq "") {Write-Host "KK"}}
> a
HI
Now if I add the type constraint to it, the $null is interpreted differently:
> function a ([string]$s=$null) {if ($s -eq $null) {Write-Host "HI"} if ($s -eq "") {Write-Host "KK"}}
> a
KK
I can't find doc that explain this. It's also not consistent.
In your first example (function a), $s is equivalent to $null - it's truly null.
In your second example (function b), because you're casting $s to a [string] object, it's actually an empty String (equivalent to [String]::Empty), not $null.
You can check this by adding the following to each of your functions:
if($s -eq [String]::Empty){"empty!"};
Only b will print empty! - a will evaluate this to $false
Alternately, add this:
$s|get-member
a will actually throw an error - the same error you'll get if you run $null|get-member. b will show you that $s is a string and list all of the members of that class.

powershell logical operators and $Error variable

Recently I had to check whether some errors occurred when my script is executed. First I've tried to check whether $Error is $null. The strange thing for me was that I haven't got any result from (neither True, nor False). Then i've wrote:
if (($error -eq $null) -or ($error -ne $null)) {Write-Host "NULL"}
And nothing was in the output. This made me very confused. I've found that such thing happens for all variables which are of System.Collections.ArrayList type.
Maybe someone knows the explanation why this happens, because for me this looks like a bug?
Version of Powershell, on which I found this, is 3.0.
#mjolinor's answer tries to explain it, but is incomplete.
When you do (1,2,3) -eq 1, you get back 1. In this case what -eq does with an array is to return the element that is equal to the RHS, and nothing if no match occurs.
On the other hand, if you do 1 -eq (1,2,3), you get False, because the above occurs only when the array is the LHS. So it is not true that the -eq operator always does behaves like the above case when it comes to arrays.
Now, coming on to the -ne usage. When you do (1,2,3) -ne 1, you get the array 2,3. That is, it returns the elements that are not equal to the RHS. And similar to -eq, 1 -ne (1,2,3), will return True
Coming to your condition - ($error -eq $null) -or ($error -ne $null)
When $error is empty, $error -eq $null will return nothing ( and is hence False in a bool statement). This is of course because there is no element matching $null in $error. Also, $error -ne $null will also return nothing ( and hence is False in a bool statement) because $error is empty and there is no element in it that is not $null.
Hence, when $error is empty, your statement will be false and the block inside if will not be executed.
If $error were not empty, either of the condition would have been true, and you would have seen the write-hostexecuted.
So how do you really solve this problem?
The straightforward way is to check the length of the $error array:
if($error.length -gt 0){
write-host "error occured"
}
Also, read this article that talks about various error handling strategies - http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/12/powershell-error-handling-and-why-you-should-care.aspx
When the -eq operator is used against an array (or arraylist), it returns all the members of the array that satisfiy the condition.
($error -eq $null) says "I want all the members of the $error arraylist that are nulls." It can't return anything but $null.
When you use it in an IF, the result is going to be cast as [bool]. $Null evaluates to $false when cast as [bool].
($error -eq $null) can never be True.
$x = new-object collections.arraylist
[void]$x.Add('a')
[void]$x.add('b')
($x -eq $null).count
[bool]($x -eq $null)
[void]$x.Add($Null)
($x -eq $null).count
[bool]($x -eq $null)
0
False
1
False

In PowerShell, how can I test if a variable holds a numeric value?

In PowerShell, how can I test if a variable holds a numeric value?
Currently, I'm trying to do it like this, but it always seems to return false.
add-type -Language CSharpVersion3 #'
public class Helpers {
public static bool IsNumeric(object o) {
return o is byte || o is short || o is int || o is long
|| o is sbyte || o is ushort || o is uint || o is ulong
|| o is float || o is double || o is decimal
;
}
}
'#
filter isNumeric($InputObject) {
[Helpers]::IsNumeric($InputObject)
}
PS> 1 | isNumeric
False
You can check whether the variable is a number like this: $val -is [int]
This will work for numeric values, but not if the number is wrapped in quotes:
1 -is [int]
True
"1" -is [int]
False
If you are testing a string for a numeric value then you can use the a regular expression and the -match comparison. Otherwise Christian's answer is a good solution for type checking.
function Is-Numeric ($Value) {
return $Value -match "^[\d\.]+$"
}
Is-Numeric 1.23
True
Is-Numeric 123
True
Is-Numeric ""
False
Is-Numeric "asdf123"
False
Modify your filter like this:
filter isNumeric {
[Helpers]::IsNumeric($_)
}
function uses the $input variable to contain pipeline information whereas the filter uses the special variable $_ that contains the current pipeline object.
Edit:
For a powershell syntax way you can use just a filter (w/o add-type):
filter isNumeric($x) {
return $x -is [byte] -or $x -is [int16] -or $x -is [int32] -or $x -is [int64] `
-or $x -is [sbyte] -or $x -is [uint16] -or $x -is [uint32] -or $x -is [uint64] `
-or $x -is [float] -or $x -is [double] -or $x -is [decimal]
}
You can do something like :
$testvar -match '^[0-9]+$'
or
$testvar -match '^\d+$'
Returns True if $testvar is a number.
If you want to check if a string has a numeric value, use this code:
$a = "44.4"
$b = "ad"
$rtn = ""
[double]::TryParse($a,[ref]$rtn)
[double]::TryParse($b,[ref]$rtn)
Credits go here
PS> Add-Type -Assembly Microsoft.VisualBasic
PS> [Microsoft.VisualBasic.Information]::IsNumeric(1.5)
True
http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.information.isnumeric.aspx
-is and -as operators requires a type you can compare against. If you're not sure what the type might be, try to evaluate the content (partial type list):
(Invoke-Expression '1.5').GetType().Name -match 'byte|short|int32|long|sbyte|ushort|uint32|ulong|float|double|decimal'
Good or bad, it can work against hex values as well (Invoke-Expression '0xA' ...)
filter isNumeric {
$_ -is [ValueType]
}
-
1 -is [ValueType]
True
"1" -is [ValueType]
False
-
function isNumeric ($Value) {
return $Value -is [ValueType]
}
isNumeric 1.23
True
isNumeric 123
True
isNumeric ""
False
isNumeric "asdf123"
False
-
(Invoke-Expression '1.5') -is [ValueType]
$itisint=$true
try{
[int]$vartotest
}catch{
"error converting to int"
$itisint=$false
}
this is more universal, because this way you can test also strings (read from a file for example) if they represent number. The other solutions using -is [int] result in false if you would have "123" as string in a variable. This also works on machines with older powershell then 5.1
If you know the numeric type you want to test against (such as int for example in the code below), you can do it like this:
> [bool]("42" -as [int])
True
> [bool](42 -as [int])
True
> [bool]("hi" -as [int])
False
But note:
> [bool](42.1 -as [int])
True
Careful!:
It was pointed out that the code above fails to identify 0 as an int. You would need to add a guard for 0:
> $n -eq 0 -or $n -as [int]
Where $n is the object you are testing.
Thank you all who contributed to this thread and helped me figure out how to test for numeric values. I wanted to post my results for how to handle negative numbers, for those who may also find this thread when searching...
Note: My function requires a string to be passed, due to using Trim().
function IsNumeric($value) {
# This function will test if a string value is numeric
#
# Parameters::
#
# $value - String to test
#
return ($($value.Trim()) -match "^[-]?[0-9.]+$")
}
I ran into this topic while working on input validation with read-host. If I tried to specify the data type for the variable as part of the read-host command and the user entered something other than that data type then read-host would error out. This is how I got around that and ensured that the user enters the data type I wanted:
do
{
try
{
[int]$thing = read-host -prompt "Enter a number or else"
$GotANumber = $true
}
catch
{
$GotANumber = $false
}
}
until
($gotanumber)
"-123.456e-789" -match "^\-?(\d+\.?\d*)(e\-?\d+)?$|^0x[0-9a-f]+$"
or
"0xab789" -match "^\-?(\d+\.?\d*)(e\-?\d+)?$|^0x[0-9a-f]+$"
will check for numbers (integers, floats and hex).
Please note that this does not cover the case of commas/dots being used as separators for thousands.
Each numeric type has its own value. See TypeCode enum definition:
https://learn.microsoft.com/en-us/dotnet/api/system.typecode?view=netframework-4.8
Based on this info, all your numeric type-values are in the range from 5 to 15.
This means, you can write the condition-check like this:
$typeValue = $x.getTypeCode().value__
if ($typeValue -ge 5 -and $typeValue -le 15) {"x has a numeric type!"}
Testing if a value is numeric or a string representation of a numeric value.
function Test-Number
{
Param
(
[Parameter(Mandatory=$true,
Position=0)]
[ValidatePattern("^[\d\.]+$")]
$Number
)
$Number -is [ValueType] -or [Double]::TryParse($Number,[ref]$null)
}
Testing if a value is numeric.
function Test-Number
{
Param
(
[Parameter(Mandatory=$true,
Position=0)]
[ValidatePattern("^[\d\.]+$")]
$Number
)
$Number -is [ValueType]
}