PowerShell:写入输出仅写入一个对象

Sco*_*oit 4 powershell

我正在学习PowerShell,并且阅读的大量文章强烈反对使用写入主机来告诉我这是“错误的做法”,并且几乎总是可以用另一种方式显示输出。

因此,我接受了建议并尝试避免使用写入主机。我发现的一个建议是改用写输出。据我了解,这会将所有内容放入管道中,并且输出在脚本(?)的末尾执行。

但是,我在输出所需内容时遇到问题。此示例演示了该问题:

$properties = @{'OSBuild'="910ef01.2.8779";
                'OSVersion'="CustomOS 3";
                'BIOSSerial'="A5FT-XT2H-5A4B-X9NM"}
$object = New-Object –TypeName PSObject –Prop $properties
Write-output $object

$properties = @{'Site'="SQL site";
                'Server'="SQL Server";
                'Database'="SQL Database"}
$object = New-Object –TypeName PSObject –Prop $properties
Write-Output $object
Run Code Online (Sandbox Code Playgroud)

这样,我可以很好地输出显示OS数据的第一个对象,但是永远不会显示包含SQL数据的第二个对象。我试过重命名变量名和其他很多东西,但是没有运气。

在对这个问题进行故障排除时,我发现了类似的问题,并提出了一些建议,就是只用写主机代替写输出。这让我很困惑。为什么有些人强烈劝阻写主机,而其他人却鼓励写主机?

以及如何准确地以时尚的方式输出这两个对象?我不完全了解写输出的流水线机制。

mkl*_*nt0 5

  • 只是澄清一下:问题仅是显示问题:

    • 在输出到控制台时,如果第一个对象是表格式的(如果Format-Table已应用,则在您的情况下隐式发生),显示列将基于第一个对象的属性被锁定。
      由于第二个输出对象与第一个输出对象不共享任何属性,因此它对表格显示没有任何贡献,因此实际上是不可见的。
    • 相反,如果您以编程方式处理脚本的输出-将其分配给变量或通过管道将其输出发送给另一个命令-两个对象都将存在。
  • 解决显示问题的最简单方法是显式格式化每个输入对象的单独显示 -参见下文。


对于脚本中给定的单个对象,您可以使用以下命令强制将格式显示(显示到主机)Out-Host:

$object | Out-Host # same as: Write-Output $object | Out-Host
Run Code Online (Sandbox Code Playgroud)

但是请注意,这只会直接且始终输出到控制台,并且该对象然后不属于脚本数据输出(将对象写入成功输出流,即带有索引的流1)。
换句话说:如果您尝试将脚本的输出分配给变量或将其输出发送给管道中的另一个命令,则该对象将不存在。

见下面为什么Out-Host最好的Write-Host,为什么它的更好,以避免Write-Host在大多数情况下。

要将技术临时应用于整个给定脚本的输出,以确保您看到所有输出对象,请使用:

./some-script.ps1 | % { $_ | Out-String }  # % is the built-in alias of ForEach-Object
Run Code Online (Sandbox Code Playgroud)

请注意,您也可以在这里使用Out-Host,但是使用的好处Out-String是,如果需要,它仍然允许您捕获文件中的显示形式。

这是一个简单的辅助函数(过滤器),您可以将其放入$PROFILE:

# Once defined, you can use: ./some-script.ps1 | Format-Each
Filter Format-Each { $_ | Out-String }
Run Code Online (Sandbox Code Playgroud)

PetSerAl的建议- ./some-script.ps1 | Format-List原则上也可以使用,但是它将输出从通常的表样式输出切换为列表样式输出,每个属性都在其自己的行中列出,这可能是不希望的。
相反,Format-Each如果输出对象是(隐式)表格式的,则为每个对象打印一个标题。


为什么Write-Output没有帮助:

Write-Output这无济于事,因为它会写入输出对象在默认情况下的位置:前面提到的成功输出流,数据应该在哪里。

如果未以某种形式重定向或捕获输出流的objets,则默认情况下会将它们发送到主机(通常是console),并在其中应用自动格式化。

另外,Write-Output很少需要使用,因为根本不捕获或重定向命令或表达式会隐式地写入成功流。把它的另一种方式:
Write-Output被暗示。

因此,以下两个语句是等效的:

Write-Output $object  # write $object to the success output stream
$object               # same; *implicitly* writes $object to the success output stream
Run Code Online (Sandbox Code Playgroud)

为什么Write-Host在这里以及通常是不明智的使用:

假设你知道使用的影响Write-Host一般-见下文-你可以用它手头的问题,但Write-Host应用简单.ToString()的格式,以它的输入,这并没有给你好看,多行格式化PowerShell将适用默认。
因此,Out-Host(和Out-String)在上面使用,因为它们确实应用了相同的友好格式。

对比以下两个语句,它们显示一个哈希表([hashtable])文字:

# (Optional) use of Write-Output: The friendly, multi-line default formatting is used.
# ... | Out-Host and ... | Out-String would print the same.
PS> Write-Output @{ foo = 1; bar = 'baz' }

Name                           Value
----                           -----
bar                            baz
foo                            1

# Write-Host: The hashtable's *entries* are *individually* stringified
#             and the result prints straight to the console.
PS> Write-Host @{ foo = 1; bar = 'baz' }

System.Collections.DictionaryEntry System.Collections.DictionaryEntry    
Run Code Online (Sandbox Code Playgroud)

Write-Host 在这里做了两件事,导致几乎没有用的输出:

  • 该[hashtable]实例的条目进行了列举,并且每个项目被单独字符串化。

  • .ToString()哈希表条目(键-值对)的字符串化为System.Collections.DictionaryEntry,即只是实例的类型名称。

Write-Host 通常避免的主要原因是:

  • 它直接输出到主机(控制台),而不输出到PowerShell的成功输出流。

    • 作为初学者,您可能会错误地认为这Write-Host是为了写入结果(数据),但事实并非如此。
  • 在绕过PowerShell的流系统时,Write-Host无法重定向输出-也就是说,既不能 抑制也不可以捕获输出(在文件或变量中)。

    • 就是说,从PowerShell v5.0开始,您现在可以通过新引入的信息流(数字6;例如;./some-script.ps1 6>write-host-output.txt)重定向其输出。但是,该流更适合与新Write-Informationcmdlet一起使用。
      相比之下,Out-Host输出仍然无法重定向。

剩下的以下合法用途Write-Host:

  • 创建最终用户提示和彩色的仅供显示的表示形式:

    • 您的脚本可能具有交互式提示,可以从用户那里征求信息。鉴于提示字符串不应成为脚本输出的一部分,并且用户还可以通过主机(通常通过)提供输入,因此使用Write-Host-(可选地通过-ForegroundColor和-BackgroundColor参数进行着色)是合适的Read-Host。

    • 同样,您可以使用Write-Host选择性着色来显式创建更友好的“仅显示”表示形式。

  • 快速原型制作:如果您想通过一种快捷方式直接将状态/诊断信息写入控制台,而又不干扰脚本的数据输出。