2

Powershell 似乎将 select cmdlet 绑定到默认输出流,并且在命令完成后不释放它,从而以意想不到的方式影响未来的命令。它如何做到这一点并不一致,这使得在编写脚本时如果没有明确指示输出就不可能生成可预测的输出。

如果您将命令放在不同的行上,混合使用不同的对象生成 cmdlet 等,就会发生这种奇怪的情况。如果您以交互方式在不同的行上运行命令,则不会发生这种情况,并且在命令行上自动创建的对象也不会发生这种情况,除非您混合在命令行上未自动创建的对象。

我给出了命令行交互式示例,但是如果您将这些命令放入一个脚本中,每个命令位于一个单独的行上,然后运行该脚本,就会发生这种情况。

PS /home/dennis> get-date|select dayofweek ; get-date

DayOfWeek
---------
   Monday
   Monday
PS /home/dennis> "string1"|select length ; "string2"

Length
------
     7
string2

为了好玩,看看这个:

S /home/dennis> "string0" ;"string1"|select length ; get-host ;"string2" ;get-date; 567 ; get-host
string0

Length
------
     7
     1
string2
     1
567
     1

PS /home/dennis> cat test.ps1
"string0"
"string1"|select length
get-host
"string2"
get-date
567
(1..5)
get-host
PS /home/dennis> ./test.ps1
string0

Length
------
     7
     1
string2
     1
567
1
2
3
4
5
     1



...

这也会影响不同类型的对象,事实上,它会影响甚至没有 select 语句中的属性的对象。延迟不是一个选项,使用 out-host 或 write-host 显式强制输出将直接写入 powershell 输出设备,因此创建将用于在管道中生成对象的脚本毫无用处。它也会混淆变量。观察:

PS /home/dennis> $d = get-date | select dayofweek ; $e = get-date ; $d ; $e

DayOfWeek
---------
   Monday
   Monday

PS /home/dennis> $d

DayOfWeek
---------
   Monday

PS /home/dennis> $e

Monday, August 5, 2019 12:33:47 PM


对于那些正在思考的人来说,这只是一个显示问题,并且可以编写脚本以正确显示它,我再说一遍,这使得脚本作为您可以在其他脚本中重复使用的工具毫无用处。观察脚本中的管道如何影响独立交互式 shell 中的命令。

PS /home/dennis> cat test.ps1                    
"string0"
"string1"|select length
get-host
"string2"
get-date
567
get-host

PS /home/dennis> ./test.ps1|% {$_}               
string0

Length
------
     7
     1
string2
     1
567
     1

PS /home/dennis> ./test.ps1|% {write-host $_}
string0
@{Length=7}
System.Management.Automation.Internal.Host.InternalHost
string2
8/5/19 12:50:54 PM
567
System.Management.Automation.Internal.Host.InternalHost
PS /home/dennis> ./test.ps1|% {$_|out-host}  
string0

Length
------
     7


Name             : ConsoleHost
Version          : 6.2.2
InstanceId       : 4e46c643-1a9d-4c55-9151-b311f287a9cb
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-US
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace


string2

Monday, August 5, 2019 1:20:24 PM

567

Name             : ConsoleHost
Version          : 6.2.2
InstanceId       : 4e46c643-1a9d-4c55-9151-b311f287a9cb
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-US
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace

在任何 shell 脚本中,我希望一个命令将独立于前一个命令执行。WhatTheFortran 是这种行为背后的逻辑?避免这种不可预测性的官方建议是什么?

4

1 回答 1

0

我知道这很令人困惑。任何时候 select-object 似乎输出一个表格, format-table 实际上是在后台运行。这些格式命令是一种错觉,由格式文件控制。但是下面的物体还是一样的。

$a = [pscustomobject]@{name='Joe'}
$b = [pscustomobject]@{address='here'}
$a,$b | format-table

name
----
Joe


$a,$b | format-list 

name : Joe

address : here

其他解决方法:

$(get-date|select dayofweek ; get-date) | format-list
$("string1"|select length ; "string2") | format-list
$("string0" ;"string1"|select length ; get-host ;"string2" ;get-date; 567 ; get-host) | format-list
test.ps1 | format-list

$d 示例的问题在于,对 $d 的赋值只是到第一个分号的第一条语句。这是一个不同的问题。

$d = $(get-date | select dayofweek ; $e = get-date ; $d ; $e)
$d | format-list  # 3 objects, unless you repeat the last line

这样的示例有效,因为这两个命令都输出具有 .format.ps1xml 文件的对象类型。一旦你使用了 select-object,输出就是一个通用的 PSCustomObject。实际上,您可以尝试在任何脚本中将 get-date 放在首位。

get-date;get-host

来自 Windows Powershell in Action:

Out-Default uses steppable pipelines to run the formatter 
cmdlets [like format-table] to do its rendering and then 
calls Out-Host to display the formatted output.
于 2019-08-06T13:12:49.770 回答