Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Write-Output

Tags:

powershell

I'm trying to write a PowerShell script that will compare two variables, one containing a list of services currently running and the other a pre-defined list of ones that should to see the differences. I want to display the differences whilst also displaying the current services running.

$compared = Compare-Object $spServices $spServices23 -SyncWindow 0
Write-Output $compared

if($sTask -eq "Read")
{

    foreach ($spService in $spServices)
    {
        $out = new-object psobject
        $out | add-member noteproperty TypeName $spService.TypeName
        $out | add-member noteproperty Status $spService.Status
        Write-Output $out
    }

}

However, when I output the Compare-Object results it shows them but then comes up blank for the output of the $out variable. Any help on how I can do this whilst keeping the output formatted.

like image 214
Rocheyy Avatar asked Mar 03 '26 16:03

Rocheyy


1 Answers

PowerShell ALWAYS does its best to try to make sure it converts output into the most useful format. One of the ways it does this is by seeing the type of object that it is first displaying in a function, and ensuring that all future objects also match this format. Sometimes it's possible, and sometimes it's not.

In the case of your code, PowerShell executes and then tries to emit the results of Compare-Object, and succeeds. 'Compare-Object' emits an object that has these properties.

Name          MemberType   Definition                                                         
----          ----------   ----------                                                         
Equals        Method       bool Equals(System.Object obj)                                     
GetHashCode   Method       int GetHashCode()                                                  
GetType       Method       type GetType()                                                     
ToString      Method       string ToString()                                                  
InputObject   NoteProperty System.ServiceProcess.ServiceController InputObject=AdobeARMservice
SideIndicator NoteProperty string SideIndicator==>                                            

These properties set the stage for what can also be emitted within this command, unless you do some fancy tricks. The reason you're not seeing the output of your later commands is that they don't also output the same properties.

To illustrate this quirk in action, see this code:

function Ham2{
        [pscustomobject]@{Name='FoxDeploy';Job="Coder"}

        [pscustomobject]@{Name='Ham';Profession="Coder"}

}

When this executes, the properties of the FIRST object emitted determine what gets displayed later on in the code. For example:

>ham2

Name      Job  
----      ---  
FoxDeploy Coder
Ham            

Working around this

There are a few ways to work around this.

First and foremost, a PowerShell best practice is that your scripts should ONLY emit one type of object. This is why functions have an .OUTPUT declaration available in their Help and [CmdletBinding()], PowerShell expects a command to issue only one type of object, plus maybe some -Verbose, or ErrorStream messages.

If you REALLY want to emit two types of objects, you could ensure that the first object has all of the properties you might ever want to display. Going back to my earlier example, if I added a Profession property to the first object, now my second object's Profession property will now become visible.

 function Ham2{
        [pscustomobject]@{Name='FoxDeploy';Job="Coder";Profession=''}

        [pscustomobject]@{Name='Ham';Profession="Coder"}

}

PS C:\Users\Stephen> ham2

Name      Job   Profession
----      ---   ----------
FoxDeploy Coder           
Ham             Coder     

Probably what you want but not recommended

If you REALLY want to emit two or more different types of objects (which you surely don't, right?) then you can get around this quirk by using Format-List or Format-Table. Be warned: these convert the output into text formatting commands and you'll lose Object properties and people will generally think it was a hacky thing to do. But it will work.

    function iFeelIcky{
    $spservices = gsv AdobeARMservice,bits
    $compared = Compare-Object (get-service bits,winrm) $spservices -SyncWindow 0
    $compared | Format-Table


        foreach ($spService in $spServices)
        {
            $out = new-object psobject
            $out | add-member noteproperty DisplayName $spService.DisplayName
            $out | add-member noteproperty Status $spService.Status
            $out
        }

    }

C:\Users\Stephen> iFeelIcky

InputObject     SideIndicator
-----------     -------------
AdobeARMservice =>           
bits            <=           
bits            =>           
winrm           <=           



DisplayName                              Status
-----------                              ------
Adobe Acrobat Update Service            Running
Background Intelligent Transfer Service Running
}

I hope that helped! Let me know if you'd like me to dive deeper or for some reason want me to stay up on this soap box :-)

like image 110
FoxDeploy Avatar answered Mar 06 '26 09:03

FoxDeploy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!