PowerShell:写输出只写一个对象 [英] PowerShell: write-output only writes one object

查看:42
本文介绍了PowerShell:写输出只写一个对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习 PowerShell,我阅读的大量文章强烈反对使用 write-host,告诉我这是不好的做法",而且几乎总是可以以另一种方式显示输出.

所以,我接受了建议并尽量避免使用写主机.我发现的一个建议是改用写输出.据我了解,这将所有内容都放在一个管道中,并在脚本的末尾执行输出 (?).

但是,我在输出我想要的内容时遇到问题.此示例演示了该问题:

$properties = @{'OSBuild'="910ef01.2.8779";'OSVersion'="CustomOS 3";'BIOSSerial'="A5FT-XT2H-5A4B-X9NM"}$object = New-Object –TypeName PSObject –Prop $properties写输出 $object$properties = @{'Site'="SQL 站点";'服务器'="SQL Server";'数据库'="SQL 数据库"}$object = New-Object –TypeName PSObject –Prop $properties写输出 $object

通过这种方式,我得到了第一个显示 OS 数据的对象的不错输出,但从未显示包含 SQL 数据的第二个对象.我试过重命名变量名和其他一些不同的东西,但没有成功.

在解决这个问题时,我发现了类似的问题,建议只用写主机替换写输出.这让我很困惑.为什么有些人强烈反对写主机,而另一些人则鼓励它?

我究竟如何以时尚的方式输出这两个对象?我对写输出的管道机制不是很了解.

解决方案

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

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


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

$object |Out-Host # 同:Write-Output $object |外主机

但是请注意,这直接且总是向控制台输出,并且该对象不是脚本数据输出的一部分(写入成功输出流的对象,索引为1的流).
换句话说:如果您尝试将脚本的输出分配给变量或将其输出发送给管道中的另一个命令,则该对象将不存在.

有关为什么 Out-HostWrite-Host 更可取,以及为什么最好避免 Write-Host 的原因,请参见下文在大多数情况下.

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

./some-script.ps1 |% { $_ |Out-String } # % 是 ForEach-Object 的内置别名

请注意,这里也可以使用 Out-Host,但是使用 Out-String 的优点是它仍然允许您在一个文件,如果需要.

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

# 一旦定义,就可以使用: ./some-script.ps1 |格式-每个过滤格式-Each { $_ |外串 }

PetSerAl 的建议 - ./some-script.ps1 |Format-List - 原则上也可以工作,但它将输出从通常的 table 样式输出切换为 list 样式输出,每个属性都列在它自己的线路,这可能是不受欢迎的.
然而,相反,Format-Each,如果输出对象是(隐式)表格格式,则为 每个 对象打印一个 header.上>


为什么 Write-Output 没有帮助:

Write-Output 没有帮助,因为它无论如何都会写入输出对象默认去的地方:前面提到的成功输出流,数据应该去的地方.

如果输出流的对象没有以某种形式重定向或捕获,它们会被默认发送到主机(通常是控制台),在那里应用自动格式.

此外,Write-Output 的使用很少必要,因为只是不捕获或重定向命令或表达式隐式 写入成功流;另一种说法:
Write-Output隐含的.

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

Write-Output $object # 将 $object 写入成功输出流$object # 相同;*隐式*将 $object 写入成功输出流


为什么使用写-Host 是不明智的,无论是在这里还是一般来说:

假设您确实知道使用 Write-Host 的一般含义 - 见下文 - 您可以使用它来解决手头的问题,但是 Write-Host 应用简单的 .ToString() 格式到它的输入,它不会给你漂亮的多行格式PowerShell 默认适用.
因此,上面使用了 Out-Host(和 Out-String),因为它们确实应用了相同的、友好的格式.

对比以下两个打印哈希表 ([hashtable]) 文字的语句:

#(可选)使用写输出:使用友好的多行默认格式.# ... |外主机和... |Out-String 将打印相同的内容.PS>写输出@{ foo = 1;酒吧 = '巴兹' }名称值---- -----巴兹富 1# 写主机:哈希表的*条目*是*单独*字符串化的# 并且结果直接打印到控制台.PS>写主机@{ foo = 1;酒吧 = '巴兹' }System.Collections.DictionaryEntry System.Collections.DictionaryEntry

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-Information cmdlet 一起使用.
      相比之下,Out-Host 输出仍然无法重定向.

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

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

    • 您的脚本可能具有向用户征求信息的交互式提示;使用 Write-Host - 可选择通过 -ForegroundColor-BackgroundColor 参数着色 - 是合适的,因为提示字符串不应成为脚本的输出和用户也通过主机提供他们的输入(通常通过 Read-Host).

    • 同样,您可以使用带有选择性着色的 Write-Host 来明确创建更友好的 for-display-only 表示.

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

I'm learning PowerShell and a vast number of articles I read strongly discourages the use of write-host telling me it's "bad practice" and almost always, the output can be displayed in another way.

So, I'm taking the advice and try to avoid use of write-host. One suggestion I found was to use write-output instead. As far as I understand, this puts everything in a pipeline, and the output is executed at the end of the script (?).

However, I have problems outputting what I want. This example demonstrates the issue:

$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

This way I get a nice output of the first object displaying the OS data, but the second object containing the SQL data is never displayed. I've tried renaming the variable names, and a bunch of other different stuff, but no luck.

While troubleshooting this problem, I found similar problems with suggestions to just replace write-output with write-host. This gets me very confused. Why are some people strongly discouraging write-host, while other people encourage it?

And how exactly do I output these two objects in a fashionably manner? I do not fully understand the pipeline mechanism of write-output.

解决方案

  • Just to clarify: the problem is only a display problem:

    • When outputting to the console, if the first object is table-formatted (if Format-Table is applied, which happens implicitly in your case), the display columns are locked in based on that first object's properties.
      Since your second output object shares no properties with the first one, it contributes nothing to the table display and is therefore effectively invisible.
    • By contrast, if you programmatically process the script's output - assign it to a variable or send its output through the pipeline to another command - both objects will be there.
  • The simplest solution to the display problem is to explicitly format for display each input object individually - see below.


For a given single object inside a script, you can force formatted to-display (to-host) output with Out-Host:

$object | Out-Host # same as: Write-Output $object | Out-Host

Note, however, that this outputs directly and invariably to the console only and the object is then not part of the script's data output (the objects written to the success output stream, the stream with index 1).
In other words: if you try to assign the script's output to a variable or send its output to another command in a pipeline, that object won't be there.

See below for why Out-Host is preferable to Write-Host, and why it's better to avoid Write-Host in most situations.

To apply the technique ad hoc to a given script's output as a whole, so as to make sure you see all output objects, use:

./some-script.ps1 | % { $_ | Out-String }  # % is the built-in alias of ForEach-Object

Note that here too you could use Out-Host, but the advantage of using Out-String is that it still allows you to capture the for-display representation in a file, if desired.

Here's a simple helper function (filter) that you can put in your $PROFILE:

# Once defined, you can use: ./some-script.ps1 | Format-Each
Filter Format-Each { $_ | Out-String }

PetSerAl's suggestion - ./some-script.ps1 | Format-List - works in principle too, but it switches the output from the usual table-style output to list-style output, with each property listed on its own line, which may be undesired.
Conversely, however, Format-Each, if an output object is (implicitly) table-formatted, prints a header for each object.


Why Write-Output doesn't help:

Write-Output doesn't help, because it writes to where output objects go by default anyway: the aforementioned success output stream, where data should go.

If the output stream's objets aren't redirected or captured in some form, they are sent to the host by default (typically, the console), where the automatic formatting is applied.

Also, use of Write-Output is rarely necessary, because simply not capturing or redirecting a command or expression implicitly writes to the success stream; another way of putting it:
Write-Output is implied.

Therefore, the following two statements are equivalent:

Write-Output $object  # write $object to the success output stream
$object               # same; *implicitly* writes $object to the success output stream


Why use of Write-Host is ill-advised, both here and often in general:

Assuming you do know the implications of using Write-Host in general - see below - you could use it for the problem at hand, but Write-Host applies simple .ToString() formatting to its input, which does not give you the nice, multi-line formatting that PowerShell applies by default.
Thus, Out-Host (and Out-String) were used above, because they do apply the same, friendly formatting.

Contrast the following two statements, which print a hash-table ([hashtable]) literal:

# (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    

Write-Host did two things here, which resulted in near-useless output:

  • The [hashtable] instance's entries were enumerated and each entry was individually stringified.

  • The .ToString() stringification of hash-table entries (key-value pairs) is System.Collections.DictionaryEntry, i.e., simply the type name of the instance.

The primary reasons for avoiding Write-Host in general are:

  • It outputs directly to the host (console) rather than to PowerShell's success output stream.

    • As a beginner, you may mistakenly think that Write-Host is for writing results (data), but it isn't.
  • In bypassing PowerShell's system of streams, Write-Host output cannot be redirected - that is, it can neither be suppressed nor captured (in a file or variable).

    • That said, starting with PowerShell v5.0, you can now redirect its output via the newly introduced information stream (number 6; e.g., ./some-script.ps1 6>write-host-output.txt); however, that stream is more properly used with the new Write-Information cmdlet.
      By contrast, Out-Host output still cannot be redirected.

That leaves just the following legitimate uses of Write-Host:

  • Creating end-user prompts and colored for-display-only representations:

    • Your script may have interactive prompts that solicit information from the user; using Write-Host - optionally with coloring via the -ForegroundColor and -BackgroundColor parameters - is appropriate, given that prompt strings should not become part of the script's output and users also provide their input via the host (typically via Read-Host).

    • Similarly, you can use Write-Host with selective coloring to explicitly create friendlier for-display-only representations.

  • Quick prototyping: If you want a quick-and-dirty way to write status/diagnostic information directly to the console without interfering with a script's data output.

这篇关于PowerShell:写输出只写一个对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆