我正在尝试创建一个简单的单行Powershell命令,该命令将列出给定进程名称的所有TCP和UDP端口。
如果我一行一行地运行,它将产生预期的输出。如果将所有四行代码放在一行中,并使用分号将各行分开,则会产生不同的结果。请参见下面的两个代码示例:
这里是设置进程名称的四行,使用该进程名称基于进程名称获取一些PID。然后,使用过程PID来过滤底部两个命令的结果。
$processName = "outlook"
$processIds = Get-Process $processName
Get-NetTCPConnection | ? {$_.OwningProcess -in $processIds.Id}
Get-NetUDPEndpoint | ? {$_.OwningProcess -in $processIds.Id}
以上工作。如果将所有这些行放在一行中,并用分号将它们分开,则会得到不同的结果:
$processName = "outlook";$processIds = Get-Process $processName;Get-NetTCPConnection | ? {$_.OwningProcess -in $processIds.Id};Get-NetUDPEndpoint | ? {$_.OwningProcess -in $processIds.Id};
几乎就像是将最后两个命令相互传递。或者不确定格式是否已关闭,我不确定。我希望分号能产生与每行都一个一行地运行相同的结果。
答案 0 :(得分:1)
TLDR:它是设计用来解决一个复杂的问题(请参阅Github问题#4552:Enable formatting of different object types in same pipeline)。
在第一个示例中发生的是,每个命令分别输出其对象数据。在这种情况下,PowerShell将引用格式文件"C:\Windows\System32\WindowsPowerShell\v1.0\Modules\NetTCPIP\Tcpip.Format.ps1xml"
来查看对象的默认格式是什么。在这种情况下,从格式文件中告知PowerShell将两个对象根据其类型格式化为表。
将命令链接成一个命令时, First 对象将确定整行的输出格式。在此示例中,它将采用Microsoft.Management.Infrastructure.CimInstance#ROOT/StandardCimv2/MSFT_NetTCPConnection
格式。由于第二个命令输出的对象类型为.../MSFT_NetUDPEndpoint
,因此无法以相同的方式对其进行格式化。 即使它可以共享相同的列,因为定义了特定的对象格式,因此PowerShell默认将其输出为后备,尽力而为Format-List *
格式。
这是by design,因为很难即时确定单个格式,尤其是当您开始具有混合类型的数组时。他们已经决定,因此最好使用第一个对象来确定类型,然后使用后备方法处理不同的对象。
我们可以进行一些测试,以了解PowerShell处理格式的不同方式:
#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}
第一个:
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
这正是我们想要的。但是,当我们链接对象时,输出将是:
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 :
第一个对象正确显示,第二个对象退回到Format-List *
。现在,让我们添加一个Select
语句:
PS> $TCP | Select LocalAddress, LocalPort ; $UDP
LocalAddress LocalPort
------------ ---------
0.0.0.0 65045
0.0.0.0 56125
:: 5353
0.0.0.0 5353
在这里我们看到,由于我们正在使用PSCustomObject
语句将TCP对象转换为Select
,因此我们的$UDP
对象可以“适合”表格式 (注意:这可能是意外的,因为您不知道一个对象在哪里结束而下一个在哪里开始!)
最终的解决方法是使用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
这为我们提供了我们可能希望看到的输出,但是在同一行中链接多个对象类型仍然不是最佳实践。