If you google for PowerShell Eqv you currently will find the VBScript-to-Windows PowerShell Conversion Guide on top of the list. Yet the answer is much of technical help:
Eqv Operator
Definition: Performs a logical equivalence on two expressions.
No doubt Eqv has its uses; we’re just not sure if it has any practical uses. Although there might be a Windows PowerShell equivalent we have to be honest: we didn’t look real hard for one.
In fact, I used the Eqv operator (also written as: A↔B) a number of times in VBScript and likely would have used it in PowerShell if it existed.
Example
I have a list of groups ("HR", "SAP", "!IT", "..."). If a user is a member of all the listed groups and explicitly not a member of groups that are preceded with an escalation mark (like "!IT", which is unraveled to: $Negate = $True and $Group = "IT") a specific task should be done. The script needs to iterate through the groups and immediately break the iteration when a group condition is not met (to save time).
A command for this would have been something like:
If ($Negate -eqv (IsMember($Group))) {Break}
How can I build a logical equivalence operator with a minimum of code?
If you take definition quiet literally, you will probably already see a possible way to achieve a logical equivalence operation:
If ([Bool]$Expression1 -eq [Bool]$Expression2) {...}
But if you take a close look to the truth table you might notice that the results for Eqv are exactly the opposite of an Xor operation.
Meaning that you can also achieve logical equivalence operation with an inverted Xor:
If (!(Expression1 -xor $Expression2)) {...}
And as it doesn’t matter what you invert for an Xor (either the whole operation or one of the expressions), you can even simplify it to:
If (!Expression1 -xor $Expression2) {...}
Check
0..3 | ForEach {
$Expression1, $Expression2 = [Int]($_ / 2), [Int]($_ % 2)
New-Object PSObject -Property #{
Expression1 = [Bool]$Expression1
Expression2 = [Bool]$Expression2
Equivalence = !$Expression1 -xor $Expression2
}
} | Format-Table -AutoSize
Truth Table
Expression1 Expression2 Equivalence
----------- ----------- -----------
False False True
False True False
True False False
True True True
Note: In this solution $Null expressions are considered $False. This differs from the VBScript Eqv implementation but is consistent with other PowerShell operators that contain $Null expressions. e.g. The VBScript statement: If 1 And vbNull Then msgbox "True" Else msgbox "False", returns True where the PowerShell statement If (1 -and $Null) {"True"} Else {"False"}, returns False.
Bitwise
If you looking for a bitwise Eqv operator (which should probably be called -bEqv, if it existed), then it would be:
$Equivalence = -bNot Expression1 -bXOr Expression2 # e.g.: -bNot 3 -bXOr 5 = -7 (-bAnd 0xF = 9)
Related
function Main {
$result1 = DoWork1
$result1.GetType()
$result2 = DoWork2
$result2.GetType()
}
function DoWork1 {
$result1 = Invoke-Sqlcmd -Query "select top 1 * from customer" -ServerInstance "(localdb)\MSSQLLocalDB" -Database "Database1" -OutputAs DataTables
#assign to variable then return
return $result1
}
function DoWork2 {
#return results without assigning to variable
return Invoke-Sqlcmd -Query "select top 1 * from customer" -ServerInstance "(localdb)\MSSQLLocalDB" -Database "Database1" -OutputAs DataTables
}
Main
Here is the unexpected output:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False DataRow System.Object
True True DataTable System.ComponentModel.MarshalByValueComponent
Using a similar example from the previous Q&A, to reproduce the same behavior:
function Invoke-SqlExample {
$dtt = [System.Data.DataTable]::new()
[void] $dtt.Columns.Add('Name')
$row = $dtt.NewRow()
$row.Name = "Hello"
$dtt.Rows.Add($row)
, $dtt
}
function Main {
$result1 = DoWork1
$result1.GetType()
$result2 = DoWork2
$result2.GetType()
}
function DoWork1 {
$result1 = Invoke-SqlExample
[pscustomobject]#{
Function = $MyInvocation.MyCommand.Name
Type = $result1.GetType().Name
} | Out-Host
return $result1
# Immediate fixes:
# return , $result1
# Write-Output $result1 -NoEnumerate
# $PSCmdlet.WriteObject($result1, $false) !! Only if Advanced Function
}
function DoWork2 {
return Invoke-SqlExample
}
Main
The output you would get from this is:
Function Type
-------- ----
DoWork1 DataTable
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False DataRow System.Object
True True DataTable System.ComponentModel.MarshalByValueComponent
We can see that the unrolling of the DataTable is only done when previously assigned to a variable, even though the variable ($result1 in DoWork1 is still of the type DataTable).
This could be explained as, DoWork2 happens in a single pipeline as opposed to DoWork1 which happens in two pipelines, first the output from Invoke-SqlExample is collected in a variable, and then emitted as output (this is where the unrolling is triggered). This is based on assumptions, and may not be entirely correct.
As iRon suggested in his helpful comment from the prior answer, an immediate fix to have DoWork1 return the DataTable instance untouched (unrolled), we can use the comma operator , which will wrap the DataTable instance in an array which is then lost during enumeration (output from the function) since it is an array of one element. The other alternative would be using Write-Output -NeEnumerate. As last alternative we can also use $PSCmdlet.WriteObject(..., $false) only if the function is an advanced one.
Adding a similar example that demonstrates the same behavior, this one was provided by mclayton in his helpful comment:
function test {
, [Collections.Generic.List[string]]#(
'hello'
'world'
)
}
(& { test }).GetType() # => List`1
(& { $a = test; $a }).GetType() # => Object[]
To complement Santiago Squarzon's helpful answer and mclayton's helpful comments:
Note:
The use of return is incidental to the behavior, given that return ... is merely syntactic sugar for ...; return, i.e. outputting ..., followed by exiting the scope; at the end of a script or function, return is never required, as the function / script is exited anyway. Any statement in a script or function can produce output, due to PowerShell's implicit output behavior - see this answer.
The behavioral difference comes down this:
Invoke-Sqlcmd -Query ... is a command, so whatever it outputs is output as-is to the success output stream of the pipeline in which the enclosing function runs.
A command in PowerShell is a unit of functionality you invoke by name, which comprises cmdlets, scripts, functions, external programs, and script blocks.
Commands invariably involve a pipeline whose success output stream they directly write to - whether or not multiple commands are connected with |.
By contrast, $result1 is an expression, and if an expression evaluates to a value of a .NET type that PowerShell considers an enumerable,[1] it is enumerated when it is sent to the success output stream (of a pipeline), which in the case of a a collection means sending its elements one by one; note that the original collection type is therefore lost in the process.
As for the rationale for performing such enumeration: It is in line with the fundamental nature of PowerShell pipelines as streams of individual objects of indeterminate length - see this answer.
An expression in PowerShell is a construct that involves any combination of variable references, literals, most operators, and .NET method calls. In an of itself, an expression does not involve a pipeline, and collections and enumerables are treated as themselves. (An expression may have nested pipelines, however, if commands participate in it.)
A pipeline - and therefore enumeration - does get involved when an expression is used in the following contexts:
When you enclose the expression in $(...), the subexpression operator or #(...), the array-subexpression operator; e.g. $(1+2) or #(1+2)
Note: (...), the grouping operator does not do that when applied to an expression: in the context of a larger expression, it merely clarifies precedence. However, when applied to a command instead (only one supported, e.g., (Get-ChildItem).Count), it runs the command to completion in a nested pipeline, collects its output object(s) and either returns them as-is, if there's only one, otherwise as an [object[]] array.
When you pipe the expression to a command, using |, the pipeline operator; e.g. 1..3 | ForEach-Object { 1 + $_ }. Note that expressions are only allowed as the first segment of pipeline.
When you output the expression - whether implicitly or explicitly (with the rarely necessary Write-Output), whether in combination with return or not - from inside a command, such as in the case of outputting $result1 from your DoWork1 function.
The upshot:
If you store a command's output in a variable ($result1 = Invoke-Sqlcmd -Query ...), and later output that variable's value to the success output stream (return $result1 or just $result1 or Write-Output $result1), you potentially introduce an additional layer of enumeration.
To prevent enumeration, you can use the techniques mentioned in Santiago's answer (transitory single-element array wrapper trick constructed with unary , - , $return1 - or Write-Output's -NoEnumerate switch - Write-Output -NoEnumerate $return1)
However, this only matters for commands that output collections as a whole, as a single object, instead of streaming collections, i.e enumerating them and outputting their elements, one by one, which is the typical case.
Commands that output collections as single objects are rare, not least because the general expectation of PowerShell commands is that they indeed stream their output.
Invoke-SqlCmd -OutputAs DataTables is such a command, only because PowerShell - perhaps surprisingly - considers System.Data.DataTable instances enumerable - it conceives of it as a collection of rows, which it enumerates in the pipeline by default.[1]
[1] See the bottom section of this answer for an overview of what types PowerShell considers enumerable. In short: PowerShell has several exceptions where it considers types that declare themselves to be enumerable not enumerable, and one opposite exception: System.Data.DataTable does not declare itself enumerable, yet PowerShell enumerates it, namely as the collection of its rows stored in the .Rows property; Santiago provided the relevant source-code link.
Today (2017-05-29) I am using PowerShell 5.0.10586.117 on Windows 7 Enterprise and run the following (shortened):
$dateOfLicense = "2017-04-20"
$dateOfToday = '{0:yyyy-MM-dd}' -f (Get-Date)
$TimeDifference = [DateTime]$dateOfToday - [DateTime]$dateOfLicense
if (($TimeDifference) = 14)
{
Write-Host "test"
}
Even the difference between both days is 39, my code jumps in the if-clause and sends "test" to screen.
What am I doing wrong here?
You are assigning 14 to $TimeDifference. Instead you wan't to compare the Days property using -le:
if ($TimeDifference.Days -le 14)
{
Write-Host "test"
}
To complement Martin Brandl's helpful answer:
Like many other languages - but unlike VBScript, for instance - PowerShell uses distinct symbols for:
the assignment operator (=)
vs. the equality test operator (-eq).
This distinction enables using assignments as expressions, which is what you inadvertently did:
if ($TimeDifference = 14) ... # same as: if (($TimeDifference) = 14) ...
assigns 14 to variable $TimeDifference, as Martin explains, and, because the assignment is (of necessity, to serve as a conditional for if) enclosed in (...), returns the assigned value (the inner (...) around $TimeDifference make no difference here, however) and that value is used as the Boolean conditional for if.
That is, the (...) expression evaluated by if has value 14 - a nonzero number - and is therefore interpreted as $true in this Boolean context, irrespective of the original value of $TimeDifference.
Note:
To learn more about PowerShell's operators, run Get-Help about_Operators
To learn about how PowerShell interprets arbitrary values as Booleans in conditionals (to-Boolean coercion), see the bottom section of this answer.
To test variables or expressions that already are Booleans, just use them as-is or, if necessary, negate them with -not (!); e.g.:
if ($someBoolean) { # Better than: if ($someBoolean -eq $true)
if (-not $someBoolean) { # Better than: if ($someBoolean -eq $false)
Finally, here's a streamlined version of your code that doesn't require intermediate variables, uses a cast to convert the string to a [datetime] instance and uses [datetime]::now, the more efficient equivalent of Get-Date (though that will rarely matter).
if (([datetime]::now - [datetime] '2017-04-20').Days -eq 14) {
"test"
}
Note how "test" as a statement by itself implicitly sends output to PowerShell's (success) output stream, which prints to the console by default.
Write-Host bypasses this stream and should generally be avoided.
Not better solution of Martin, just an shorty code
$dateOfLicense = [DateTime]"2017-04-20"
$TimeDifferenceDays = ((Get-Date) - $dateOfLicense).Days
if ($TimeDifferenceDays -lt 14)
{
Write-Host "test"
}
I'm new to PowerShell script. This is the question that comes when I'm creating test using pester. The question is about comparing array vs string as follows:
#('hello', 'world') -eq 'hello world' # returns nothing
'hello world' -eq #('hello', 'world') # returns true
I will appreciate if someone can tell me the difference.
PowerShell operators follow the "left hand rule". In other words, the type of the object on the LHS determines how the comparison is done. If the LHS is an array, then the right hand side will be compared to each element of RHS array. If there are matches, then the operator returns the matching elements. if there are no matches, then the operator returns nothing. On the other hand, if the LHS is a string, then the right hand side will be converted to a string and the comparison is done. An array is converted to a string by doing the equivalent of $array -join $OFS. $OFS (output field separator) is a builtin variable that defaults to " ". This is why your second comparison succeeds #("Hello", "world") gets converted to "Hello world".
-eq returns different things when applied to different types of values.
1,2,3,4,1 -eq 1 # returns #(1, 1)
So when applied to an array on the left-hand side, it returns all elements from that array that are equal to the right-hand side operand. It works like a filter on arrays (the same applies to other comparison operators like -ne, -gt, etc.)
#('hello', 'world') -eq 'hello world' # returns nothing
Naturally.
When -eq is applied to a single value on the left-hend side, it returns $true or $false depending on whether the right-hand side is equal to it.
Type conversion takes place. For example, if the left-hand side is a string, the right-hand side is converted to a string, too.
Arrays are converted to string by converting all their elements to string and joining them with a single space by default (the character used can be changed by using a different $OFS - see MSDN, Powershell Team Blog).
'hello world' -eq #('hello', 'world') # returns true
Naturally. But:
'hello world' -eq #('hello ', 'world') # returns false, note the space
Use .Equals() to prevent that.
'hello world'.Equals( #('hello', 'world') ) # returns false
(1).Equals("1") # false, too
Today (2017-05-29) I am using PowerShell 5.0.10586.117 on Windows 7 Enterprise and run the following (shortened):
$dateOfLicense = "2017-04-20"
$dateOfToday = '{0:yyyy-MM-dd}' -f (Get-Date)
$TimeDifference = [DateTime]$dateOfToday - [DateTime]$dateOfLicense
if (($TimeDifference) = 14)
{
Write-Host "test"
}
Even the difference between both days is 39, my code jumps in the if-clause and sends "test" to screen.
What am I doing wrong here?
You are assigning 14 to $TimeDifference. Instead you wan't to compare the Days property using -le:
if ($TimeDifference.Days -le 14)
{
Write-Host "test"
}
To complement Martin Brandl's helpful answer:
Like many other languages - but unlike VBScript, for instance - PowerShell uses distinct symbols for:
the assignment operator (=)
vs. the equality test operator (-eq).
This distinction enables using assignments as expressions, which is what you inadvertently did:
if ($TimeDifference = 14) ... # same as: if (($TimeDifference) = 14) ...
assigns 14 to variable $TimeDifference, as Martin explains, and, because the assignment is (of necessity, to serve as a conditional for if) enclosed in (...), returns the assigned value (the inner (...) around $TimeDifference make no difference here, however) and that value is used as the Boolean conditional for if.
That is, the (...) expression evaluated by if has value 14 - a nonzero number - and is therefore interpreted as $true in this Boolean context, irrespective of the original value of $TimeDifference.
Note:
To learn more about PowerShell's operators, run Get-Help about_Operators
To learn about how PowerShell interprets arbitrary values as Booleans in conditionals (to-Boolean coercion), see the bottom section of this answer.
To test variables or expressions that already are Booleans, just use them as-is or, if necessary, negate them with -not (!); e.g.:
if ($someBoolean) { # Better than: if ($someBoolean -eq $true)
if (-not $someBoolean) { # Better than: if ($someBoolean -eq $false)
Finally, here's a streamlined version of your code that doesn't require intermediate variables, uses a cast to convert the string to a [datetime] instance and uses [datetime]::now, the more efficient equivalent of Get-Date (though that will rarely matter).
if (([datetime]::now - [datetime] '2017-04-20').Days -eq 14) {
"test"
}
Note how "test" as a statement by itself implicitly sends output to PowerShell's (success) output stream, which prints to the console by default.
Write-Host bypasses this stream and should generally be avoided.
Not better solution of Martin, just an shorty code
$dateOfLicense = [DateTime]"2017-04-20"
$TimeDifferenceDays = ((Get-Date) - $dateOfLicense).Days
if ($TimeDifferenceDays -lt 14)
{
Write-Host "test"
}
I'm looking for example of how I would solve the scenario below:
Imagine my printer has the following property for "Status"
0 -Offline
2 -Paper Tray Empty
4 -Toner Exhausted
8 -Paper Jam
When I query status it returns a value of 12. I can obviously see that this means the printer has the Toner Exhausted and a Paper Jam, but how would I work this out with Powershell?
Thanks
The boolean bitwise and operator in Powershell is -band.
Assume you define your values and descriptions in a hashtable, and have the value of 12 from the printer:
$status = #{1 = "Offline" ; 2 = "Paper Tray Empty" ; 4 = "Toner Exhausted" ; 8 = "Paper Jam" }
$value = 12
Then, this statement will give you the textual descriptions:
$status.Keys | where { $_ -band $value } | foreach { $status.Get_Item($_) }
You could define the enum in Powershell, but the above works just as well, and defining enums in Powershell seems like a lot of work.
Here is an article, that talks about how to use the bitwise operators in Powershell.
You can let PowerShell do more of the work for you. Here's an example using System.IO.FileOptions:
PS> [enum]::GetValues([io.fileoptions]) | ?{$_.value__ -band 0x90000000}
RandomAccess
WriteThrough