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 which echo
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 use Write-Debug
(for debugging only) or Write-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 to return
; e.g., return 0 -lt 1
is short for the following two separate statements 0 -lt 1; return
: 0 -lt 1
outputs the value of the -lt
operation, and return
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 the if
conditional evaluate to $true
always.
Because "isValidN is called"
was part of the output, and that output was captured (and consumed) by the if
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: