将PowerShell对象写入控制台时的不同行为

时间:2013-10-20 02:11:07

标签: powershell-v3.0

我有一个函数Get-Projects,它返回一个对象数组。我想将这些打印到控制台,以便用户可以选择他们感兴趣的项目。但是,我有四个场景,其中只有两个打印出所需/预期的结果。

场景1 - 表格输出,无功能

当我简单地“返回”这样的项目时,它们以表格形式按预期打印出来。这是所需的格式。

$projects = Get-Projects
$projects

# Console Output
id      name         children                                                      
--      ----         --------                                                      
1       Project 1    1 {@id=2; name=Project 2}
3       Project 3    3 {@id=4; name=Project 4}

场景二 - 没有带有写作项目功能的输出

我创建了一个名为Write-Projects的函数来封装格式化行为,以防我决定改变格式化的方式。然而,当我这样做时,没有任何东西被打印到控制台。

Function Write-Projects
{
    Param([Object[]] $projects)

    $projects
}

$projects = Get-Projects
Write-Projects $projects

# No Console Output

场景3 - 带有写项目功能的字符串输出

如果我修改Write-Projects以使用Write-Host $projects我会得到控制台输出但不是我所期望的。它似乎是我的Object数组的字符串表示。

Function Write-Projects
{
    Param([Object[]] $projects)

    Write-Host $projects
}

$projects = Get-Projects
Write-Projects $projects

# Console Output
@{id=1; name=Project 1; children=System.Object[]} @{id=2; name=Project 2; children=System.Object[]}

场景4 - 带有写入项目功能的表格输出

我发现this question可以解决问题,但我不确定原因。基本上我的Write-Projects方法现在看起来像这样。

Function Write-Projects
{
    Param([Object[]] $projects)

    Write-Host ($projects | Format-Table | Out-String)
}

$projects = Get-Projects
Write-Projects $projects

# Console Output
id      name         children                                                      
--      ----         --------                                                      
1       Project 1    1 {@id=2; name=Project 2}
3       Project 3    3 {@id=4; name=Project 4}

每个场景中发生了什么以及为什么我按照描述获得输出?

2 个答案:

答案 0 :(得分:2)

我不确定为什么方案2不起作用。只是为了测试我做了一些自定义对象并尝试了它确实对我有用:

PS> $obj1 = New-Object PSObject @{ a = 1; b = 2 }
PS> $obj2 = New-Object PSObject @{ a = 3; b = 4 }
PS> $obj1,$obj2

Name                           Value
----                           -----
a                              1
b                              2
a                              3
b                              4

PS> function write-projects { param ([object[]] $projects) $projects }
PS> write-projects -projects $obj1,$obj2

Name                           Value
----                           -----
a                              1
b                              2
a                              3
b                              4

我知道为什么方案3不起作用。这是因为当您使用Write-Host时,使用其toString()方法将对象转换为文本表示,这就是为什么您的对象不会按原样输出的原因:

PS> function write-projects { param ([object[]] $projects) write-host $projects }
PS> write-projects -projects $obj1,$obj2
System.Collections.Hashtable System.Collections.Hashtable

# notice the output of toString()
PS> $obj1.ToString()
System.Collections.Hashtable

更好的方法是使用Write-Output作为(a)自动枚举对象,以及(b)它是“管道友好的”,因为对象不是直接写入主机而是传递给下一步。

PS> function write-projects { param ([object[]] $projects) write-output $projects }
PS> write-projects -projects $obj1,$obj2

Name                           Value
----                           -----
a                              1
b                              2
a                              3
b                              4

场景4工作的原因是因为你直接输出对象 - 隐式枚举它 - 然后它被格式化并且格式化的输出是Write-Host接收到屏幕的内容。我会说你可以在方案4中跳过Write-Host它应该仍然可以工作。

PS> function write-projects { param ([object[]] $projects) $projects | format-table }
PS> write-projects -projects $obj1,$obj2

Name                           Value
----                           -----
a                              1
b                              2
a                              3
b                              4

希望有所帮助!

答案 1 :(得分:0)

这个问题有一个简单的答案和一个复杂的解决方案:

不要使用Write-Host(格式化程序除外)

Write-Host直接输出到屏幕,这可以防止您使用数据。理想情况下,您希望Get-Projects以您希望的方式显示项目,并且仍然是一个真实的对象。您可以使用格式化程序执行此操作。

当您在第一个场景中写入主机时,PowerShell正在将输入(对象列表)强制转换为字符串。

在第二个示例中,您遇到了同一个bug的嵌套版本:正在强制将子项转换为字符串,因此它适用于Format-Table。

执行此操作的正确方法是使用格式化程序。

有几种方法可以做到这一点,但我建议我几年前写的一个名为EzOut的模块。 EZOut允许您使用Cmdlet编写格式化程序,并允许您动态添加它们。

要使其工作,请修改Get-Projects的每个输出,如下所示:

$output.pstypenames.clear()
$output.pstypenames.add("Project")

然后我建议导入EZOut并编写自定义格式化程序:

Write-Format -TypeName "Project" -Action {
    $project = $_
    "
    Project Name : $($project.Name)
            ID   : $($project.ID)
            Children $(
                $(
                    $project.Children | format-Table | Out-String | Foreach-Object { "            " + $_ + "
"} 
                )
    "
} |
  Out-FormatData |
  Add-FormatData

这将创建一个格式化程序,(应该)显示缩进的子项,并将其注册。