Running Line by Line Produces Odd Result Compared to Running Lines as a Single Line with Semicolons

前端 未结 1 923
刺人心
刺人心 2021-01-24 04:23

I\'m trying to create a simple one line Powershell command that\'ll list all TCP and UDP ports of a given process name.

If I run the lines one by one, it produces the ex

1条回答
  •  隐瞒了意图╮
    2021-01-24 05:22

    TLDR: it's by design to solve a complex issue (see Github issue #4552: Enable formatting of different object types in same pipeline).

    What happens in the first example, is that each command outputs their object data individually. In this case, PowerShell will refer to the format file "C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\Tcpip.Format.ps1xml" to see what the default formatting for the object is. In this case, from the format file, PowerShell is told to format both objects to a table based on their type.

    When you chain the commands into one, the First object will determine the output format for the entire line. In this example, it will be in the Microsoft.Management.Infrastructure.CimInstance#ROOT/StandardCimv2/MSFT_NetTCPConnection format. Since the second command outputs an object of type .../MSFT_NetUDPEndpoint it cannot be formatted in the same way. Even though it could share the same columns, since the specific object formatting is defined, PowerShell then defaults to outputting the object in fallback, best effort Format-List * format.

    This is by design because it's hard to determine individual formatting on the fly especially when you start having arrays with mixed types. They have decided that it is therefore better to use the first object to determine type, and then handle different objects with a fallback.

    We can do some testing to see the different ways that PowerShell handles formatting:

    #For brevity sake, let's assign variables for our examples:
    $processName = "outlook"
    $processIds = Get-Process $processName
    $TCP = Get-NetTCPConnection | ? {$_.OwningProcess -in $processIds.Id}
    $UDP = Get-NetUDPEndpoint | ? {$_.OwningProcess -in $processIds.Id}
    

    First individually:

    PS> $TCP
    
    LocalAddress                        LocalPort RemoteAddress                       RemotePort State       AppliedSetting OwningProcess
    ------------                        --------- -------------                       ---------- -----       -------------- -------------
    0.0.0.0                             65045     0.0.0.0                             0          Bound                      24200
    0.0.0.0                             56125     0.0.0.0                             0          Bound                      24200
    
    PS> $UDP
    
    LocalAddress                             LocalPort
    ------------                             ---------
    ::                                       5353
    0.0.0.0                                  5353
    

    This is exactly what we want. However, when we chain our objects the output will be:

    PS> $TCP; $UDP
    
    LocalAddress                        LocalPort RemoteAddress                       RemotePort State       AppliedSetting OwningProcess
    ------------                        --------- -------------                       ---------- -----       -------------- -------------
    0.0.0.0                             65045     0.0.0.0                             0          Bound                      24200
    0.0.0.0                             56125     0.0.0.0                             0          Bound                      24200
    
    Caption                  :
    Description              :
    ElementName              :
    InstanceID               : ::++5353
    CommunicationStatus      :
    DetailedStatus           :
    HealthState              :
    InstallDate              :
    Name                     :
    OperatingStatus          :
    OperationalStatus        :
    PrimaryStatus            :
    Status                   :
    StatusDescriptions       :
    AvailableRequestedStates :
    EnabledDefault           : 2
    EnabledState             :
    OtherEnabledState        :
    RequestedState           : 5
    TimeOfLastStateChange    :
    TransitioningToState     : 12
    AggregationBehavior      :
    Directionality           :
    CreationTime             : 2019-04-15 9:05:09 AM
    LocalAddress             : ::
    LocalPort                : 5353
    OwningProcess            : 24200
    PSComputerName           :
    
    Caption                  :
    Description              :
    ElementName              :
    InstanceID               : 0.0.0.0++5353
    CommunicationStatus      :
    DetailedStatus           :
    HealthState              :
    InstallDate              :
    Name                     :
    OperatingStatus          :
    OperationalStatus        :
    PrimaryStatus            :
    Status                   :
    StatusDescriptions       :
    AvailableRequestedStates :
    EnabledDefault           : 2
    EnabledState             :
    OtherEnabledState        :
    RequestedState           : 5
    TimeOfLastStateChange    :
    TransitioningToState     : 12
    AggregationBehavior      :
    Directionality           :
    CreationTime             : 2019-04-15 9:05:09 AM
    LocalAddress             : 0.0.0.0
    LocalPort                : 5353
    OwningProcess            : 24200
    PSComputerName           :
    

    The first object displays properly, and the second falls back to a Format-List *. Now, let's add in a Select statement:

    PS> $TCP | Select LocalAddress, LocalPort ; $UDP
    
    LocalAddress LocalPort
    ------------ ---------
    0.0.0.0          65045
    0.0.0.0          56125
    ::                5353
    0.0.0.0           5353
    

    Here we see that since we are transforming the TCP object into a PSCustomObject, with the Select statement, our $UDP object can "fit" into the table format, it flows in with the rest of the objects in the pipeline! (Note: this could be unexpected, as you don't know where one object ends and the next begins!)

    The final workaround is to essentially "flush" the pipeline by using Out-String:

    PS> $TCP | Out-String; $UDP
    
    LocalAddress                        LocalPort RemoteAddress                       RemotePort State       AppliedSetting OwningProcess
    ------------                        --------- -------------                       ---------- -----       -------------- -------------
    0.0.0.0                             65045     0.0.0.0                             0          Bound                      24200
    0.0.0.0                             56125     0.0.0.0                             0          Bound                      24200
    
    
    LocalAddress                             LocalPort
    ------------                             ---------
    ::                                       5353
    0.0.0.0                                  5353
    

    This gives us the output we likely would like to see, but it's probably still not best practice to chain multiple object types in the same line.

    0 讨论(0)
提交回复
热议问题