I am trying to output variables using Write-Output
, but it did not work inside a PowerShell class method. Write-Host
is working. See the sample code below.
class sample {
[string] sampleMethod() {
$output = "Output message"
try {
Write-Output $output
throw "error"
}
catch {
Write-Output $output
$_
}
return "err"
}
}
$obj = [sample]::new()
$obj.sampleMethod()
Is there any specific reason why Write-Output
doesn't work inside a class method?
Write-Output sends objects to the primary pipeline, also known as the "output stream" or the "success pipeline." To send error objects to the error pipeline, use Write-Error . This cmdlet is typically used in scripts to display strings and other objects on the console.
The first method of printing output is using the Write-Output cmdlet. This cmdlet is used to pass objects in the pipeline to the successive commands. In case of the command being last, the final object is written to the console. To pass on error objects, Write-Error cmdlet is used.
In a nutshell, Write-Host writes to the console itself. Think of it as a MsgBox in VBScript. Write-Output , on the other hand, writes to the pipeline, so the next command can accept it as its input. You are not required to use Write-Output in order to write objects, as Write-Output is implicitly called for you.
The echo command is used to print the variables or strings on the console. The echo command has an alias named “Write-Output” in Windows PowerShell Scripting language. In PowerShell, you can use “echo” and “Write-Output,” which will provide the same output.
To add to marsze's excellent answer:
Think of the method signature ([string] sampleMethod()
) as a contract - you promise the user that if they call the method with 0 parameters, it'll always return exactly one [string]
object.
Allowing an arbitrary number of Write-Output
statements during method execution would violate that contract!
While write-output does not work inside a class' method, it does work if the method returns a script-block that is then executed outside, like so:
#Cmdlet you can't edit that outputs whilst running
function foo {
write-output "Beginning complex operation!";
start-sleep 2;
write-output "Important information you would rather not have to wait for!";
start-sleep 2;
write-output "Operation finished!";
}
class IsClass{
static [ScriptBlock]bar(){
#create a ScriptBlock that the must be executed outside
return { foo };
}
}
& $([IsClass]::bar());
<#Output:
Beginning complex operation!
[two second wait]
Important information you would rather not have to wait for!
[two second wait]
Operation finished!
#>
This is a relatively hacky solution. As far as I am aware, though, it is the only way of writing output of cmdlets called inside the static method when the cmdlet is still running. Usage of write-host
inside the cmdlet that the method calls is not an option if you do not have access to the cmdlets you are calling inside the class.
Example without using script blocks:
#Cmdlet you can't edit that outputs whilst running
function foo {
write-output "Beginning complex operation!";
start-sleep 2;
write-output "Important information you would rather not have to wait for!";
start-sleep 2;
write-output "Operation finished!";
}
#Class that uses the mentioned cmdlet
class IsClass{
static [void]bar(){
#Directly invoke the method
write-host $(foo);
}
}
[IsClass]::bar();
<#Output:
[Awkward 4 second pause]
Beginning complex operation! Important information you would rather not have to wait for! Operation finished!
It's also worth noting that the second method results in all output showing up on one line.
A scenario in which you may wish to actually use this is if you were writing a script that would install tools using the command line. The installation uses cmdlets that you have no control over, and that take several minutes to complete (such as installing software using chocolatey). This means that if the cmdlet's progress changes (such as moving onto installing the software's dependencies) it cannot write the change to the console until the full install has completed, leaving the user in the dark as to what is currently happening.
UPDATE: as of writing this I have also come across many issues concerning the usage of scope inside script-blocks, as they do not share the scope of the context in which they were created, only the scope in which they are executed. This quite heavily invalidates a lot of what I mentioned here, as it means you can't reference the properties of the class.
UPDATE 2: UNLESS you make use of the GetNewClosure!
static [ScriptBlock]bar(){
#create a ScriptBlock that the must be executed outside
$that = $this;
return { $that.ClassVariable }.GetNewClosure();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With