Powershell 与使用分号的单行运行相比,逐行运行会产生奇数结果

Powershell 与使用分号的单行运行相比,逐行运行会产生奇数结果,powershell,Powershell,我正在尝试创建一个简单的单行Powershell命令,该命令将列出给定进程名称的所有TCP和UDP端口 如果我逐个运行这些行,它将生成预期的输出。如果我将所有四行代码放在一行中,并使用分号分隔这些行,则会产生不同的结果。请参见下面的两个代码示例: 下面是设置流程名称的四行代码,使用该流程名称可以根据流程名称获取一些PID。然后使用进程PID过滤来自底部两个命令的结果 $processName=“outlook” $processIds=获取进程$processName 获取网络连接|?{$\.O

我正在尝试创建一个简单的单行Powershell命令,该命令将列出给定进程名称的所有TCP和UDP端口

如果我逐个运行这些行,它将生成预期的输出。如果我将所有四行代码放在一行中,并使用分号分隔这些行,则会产生不同的结果。请参见下面的两个代码示例:

下面是设置流程名称的四行代码,使用该流程名称可以根据流程名称获取一些PID。然后使用进程PID过滤来自底部两个命令的结果

$processName=“outlook”
$processIds=获取进程$processName
获取网络连接|?{$\.OwningProcess-in$processIds.Id}
获取NetUDPEndpoint |?{$\.OwningProcess-in$processIds.Id}
以上工作。如果我将所有这些行放在一行中,并用分号分隔它们,我会得到不同的结果:

$processName=“outlook”$ProcessId=获取进程$processName;获取网络连接|?{$\.OwningProcess-in$processIds.Id};获取NetUDPEndpoint |?{$\.OwningProcess-in$processIds.Id};

这几乎就好像它将最后两个命令相互传送。或者可能是格式化关闭了,我不确定。我希望分号会产生相同的结果,就好像每一行都在一行一行地运行一样。

TLDR:这是为了解决一个复杂的问题而设计的(请参见Github问题#4552:)

在第一个示例中,每个命令分别输出其对象数据。在这种情况下,PowerShell将参考格式文件
“C:\Windows\System32\WindowsPowerShell\v1.0\Modules\nettcip\Tcpip.format.ps1xml”
,查看对象的默认格式。在本例中,从格式化文件中,PowerShell被告知根据对象的类型将这两个对象格式化为一个表

将命令链接为一个命令时,第一个对象将确定整行的输出格式。在本例中,它将采用
Microsoft.Management.Infrastructure.CimInstance#ROOT/StandardCimv2/MSFT\u nettcpcconnection
格式。由于第二个命令输出的对象类型为
../MSFT_NetUDPEndpoint
,因此不能以相同的方式格式化。尽管PowerShell可以共享相同的列,但由于已定义了特定的对象格式,因此PowerShell默认情况下会以回退、尽力而为的
格式列表*
格式输出对象

这是因为很难即时确定单个格式,特别是当您开始使用混合类型的数组时。因此,他们决定最好使用第一个对象来确定类型,然后使用回退处理不同的对象

我们可以进行一些测试,以了解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           :
第一个对象正确显示,第二个对象返回到
格式列表*
。现在,让我们添加一个
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
这里我们看到,由于我们正在将TCP对象转换为
PSCustomObject
,使用
Select
语句,我们的
$UDP
对象可以“适应”表格格式,它将与管道中的其余对象一起流入!(注意:这可能是出乎意料的,因为您不知道一个对象的终点和下一个对象的起点!)

最后一种解决方法是使用
输出字符串来“刷新”管道:

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

这为我们提供了我们可能希望看到的输出,但在同一行中链接多个对象类型可能仍然不是最佳做法。

首先,您的两个示例不相同,因为您的第二个示例具有
$processIds=Get Process$进程名
而不是
$processid=Get Process$processName。除了说“这是PowerShell在控制台中为您自动格式化”之外,我无法从技术上解释为什么会发生这种情况。通过运行
Get NetUDPEndpoint |,您可以看到上一个命令的相同输出?{$\.OwningProcess-in$processIds.Id}格式列表*
。有些东西触发了格式化,使所有属性都成为列表格式,这是我无法完全解释的。哦,是的,我在问题中刚刚解决了这个问题。这不是它失败的原因,这只是问题中的一个输入错误:P谢谢你的回答。我也不完全理解,这有点奇怪。在第二个示例中,您可以预期情况会略有不同,因为在按下Enter键之前,您不会向PowerShell发送任何内容。因此,发送一个命令并对其进行处理,然后将其输出到控制台与发送所有命令并对其进行处理,然后等待一个控制台输出是有区别的。好消息是,您可以控制输出的外观,而不是依赖PowerShell来为您完成。如果您喜欢第一个输出,可以使用
$processName=“outlook”复制它$ProcessId=获取进程$processName;获取网络连接|?{$\.OwningProcess-in$processIds.Id}ft;获取NetUDPEndpoint |?{$\.OwningProcess-in$processIds.Id}ft。只需强制PowerShell格式化表格样式。所有的输出数据都是相同的,只是表现形式不同。有什么区别?我在我的环境中看不到任何变化。