There is a common misconception about how Powershell functions return data. Actually there isn't a single return value or object as you are used to from other programming languages. Instead, there is an output stream of objects.
There are several ways to add data to the output stream, e. g.:
Write-Output $data
$data
return $data
Confusing to PS newcomers coming from other languages is the fact that return $data
does not define the sole "return value" of a function. It is just a convenient way to combine Write-Output $data
with an early exit from the function. Whatever data that was written to the output stream befor the return
statement also contributes to the output of the function!
Analysis of the code
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append
... appends the InputObject to the output stream of ShowSave-Log
ShowSave-Log "`'$app_name`' is installed."
... appends the message to the output stream of Is-Installed
return $true
... appends the value $true
to the output stream of Is-Installed
Now we actually have two objects in the output stream of Is-Installed
, the string message and the $true
value!
if (Is-Installed "Notepad++ (64-bit x64)") {Write-Host "TRUE"}
Let me split up the if-statement to explain in detail what it does:
$temp = Is-Installed "Notepad++ (64-bit x64)"
... redirects the output stream of Is-Installed
to temporary variable. As the output stream has been stored into a variable, it won't go further up in the function call chain, so it won't show up in the console anymore! That's why you don't see the message from Tee-Object
.
In our case there is more than one object in the output stream, so the variable will be an array like @('... is installed', $true)
if ($temp) {Write-Host "TRUE"}
... does an implicit boolean conversion of the array $temp
. A non-empty array converts to $true
. So there is a bug here, because the function Is-Installed
always "returns" a non-empty array. When the software is not installed, $temp
would look like @('... not found ...', $false)
, which also converts to $true
!
Proof:
$temp = Is-Installed "nothing"
$temp.GetType().Name # Prints 'Object[]'
$temp[0] # Prints '2020.12.13 12:39:37 'nothing' not found ...'
$temp[1] # Prints 'False'
if( $temp ) {'Yes'} # Prints 'Yes' !!!
How can I get Tee-Object output to terminal screen?
Don't let it write to the output stream, which should be used only for actual data to be "returned" from a function, not for log messages.
A simple way to do that is to redirect the output of Tee-Object
to Write-Host
, which writes to the information stream:
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append | Write-Host
A more sensible way would be to redirect to the verbose stream:
Tee-Object -InputObject "$date $text" -FilePath $LOG_FILE -Append | Write-Verbose
Now the log message doesn't clutter the terminal by default. Instead, to see detailed logging the caller has to enable verbose output, e. g. by setting $VerbosePreference = 'Continue'
or calling the function with -Verbose
parameter:
if( Is-Installed 'foo' -Verbose ){<# do something #>}
PSVersion 5.1.19041.610
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.610
CLRVersion 4.0.30319.42000
– Doug Maurer