Got a question why logical function is not executed or evaluated at all when called from if-statement. tried with powershell version 4 and 7 in windows, both failed. i have already worked around on this, but curious how could this happen.
can anyone please help? not sure whether this is any environment setup related, or powershell grammar issue. any opinions would be appreciated.
# example 1:
function isValidN{
echo “isValidN is called”
return 0 -lt 1;
}
function Main {
if(isValidN){
echo “t”
}else{
echo “f”
}
}
Main;
#result: it will not execute isValidN at all, it did not printout “isValidN is called”, Why?
#Example 2:
function isValidN{
echo “isValidN is called”
return 0 -lt 1;
}
function Main {
isValidN;
if(isValidN){
echo “t”
}else{
echo “f”
}
}
Main;
#result: the first isValidN call before if-statement would execute and printout “isValidN is called.” However, the 2nd one in the if-statement NOT. Why?
CodePudding user response:
The crucial information is in the helpful comments by Santiago Squarzon and Mathias R. Jessen, but let me break it down conceptually:
As will become clear later, your isValidN
function was called in the if
statement, but produced no display output.
PowerShell functions and scripts do not have return values - they produce output that is typically written to the success output stream, PowerShell's analog to stdout (standard output).
In that sense, PowerShell functions more like a traditional shell rather than a typical programming language.
Unlike traditional shells, however, PowerShell offers six output streams, and targeting these streams is a way to output information that isn't data, such as debugging messages. Dedicated cmdlets exist to target each streams, as detailed in the conceptual about_Output_Streams help topic.
Any statement in a function or script may produce output - either implicitly, if output from a command call or the value of an expression isn't captured or redirected, or explicitly, with the - rarely necessary -
Write-Output
cmdlet, for whichecho
is a built-in alias.The implication is that any script or function can have an open-ended number (multiple) "return values".
By contrast,
Write-Host
is meant for printing information to the host (display, terminal), and bypasses the success output stream (since version 5 of PowerShell, it writes to the information stream, which - as all streams do - goes to the host by default). As such, it is sometimes used as a quick way to provide debugging output, though it's generally preferable to useWrite-Debug
(for debugging only) orWrite-Verbose
(to provide extended information to the user on demand), but note that making their output visible requires opt-in.
Unlike in other languages,
return
is not needed to output data - unless there is a need to exit the scope at that point, its use is optional - and may be used independently of output statements; as syntactic sugar, data to output may be passed toreturn
; e.g.,return 0 -lt 1
is short for the following two separate statements0 -lt 1; return
:0 -lt 1
outputs the value of the-lt
operation, andreturn
then exits the scope.
Applying the above to what you tried:
function isValidN {
echo "isValidN is called"
return 0 -lt 1
}
is the equivalent of:
function isValidN {
Write-Output "isValidN is called"
Write-Output (0 -lt 1)
return
}
Or, using implicit output, and omitting the redundant return
call, given that the scope is implicitly exited at the end of the function:
function isValidN {
"isValidN is called"
0 -lt 1
}
Thus, your function outputs two objects: String "isValidN is called"
, and the evaluation of 0 -lt 1
, which is $true
Using a command call (including scripts and functions) in an if
conditional - captures all output from that command, so that:
if (isValidN) # ...
effectively becomes:
if (@("isValidN is called", $true)) # ...
That is, the two-object output from function isValidN
, when captured, turned into a two-element array (of type [object[]]
), which was implicitly evaluated as a Boolean ([bool]
) due to its use with if
.
PowerShell allows any object to be converted to [bool]
, and a 2 -element array always evaluates to $true
, irrespective of its content - see the bottom section of this answer for a summary of PowerShell's to-Boolean conversion rules.
To summarize:
What you meant to print to the display,
"isValidN is called"
, became part of the function's output ("return value"), "polluting" the function's output with incidental data, making theif
conditional evaluate to$true
always.Because
"isValidN is called"
was part of the output, and that output was captured (and consumed) by theif
conditional, it never printed to the display.
What I assume you meant do to:
function isValidN {
Write-Debug 'isValidN is called'
0 -lt 1
}
# Make Write-Debug output visible.
# To turn it back off:
# $DebugPreference = 'SilentlyContinue'
$DebugPreference = 'Continue'
# Call the function
if (isValidN) { 't' } else { 'f' }
Output:
DEBUG: isValidN is called
t
Note:
- If you make a function or script an advanced one, you can turn debugging output on on a per-command call basis, by passing the common
-Debug
parameter, as an alternative to setting the$DebugPreference
preference variable, which affects all commands in the current scope and descendant scopes.