为什么管道不适用于转换后的 json 对象?

Why does the pipeline not work on converted json objects?

考虑以下代码:

$data = '[
    {
        "Name":  "banana",
        "Color":  "yellow"
    },
    {
        "Name":  "kiwi",
        "Color":  "green"
    },
    {
        "Name":  "apple",
        "Color":  "red"
    }
]'
# Returns 3 objects while only 1 was expected
$data | ConvertFrom-Json | Where-Object { $_.Name -eq 'banana' }

# Workaround, returns 1 object as expected:
($data | ConvertFrom-Json) | Where-Object { $_.Name -eq 'banana' }

为什么不能使用第一个选项?从 json 转换对象后,Where-Object 函数似乎不正确。这发生在 PowerShell 版本 5.1.

我们是否遗漏了一些明显的东西?

与:

$data | ConvertFrom-Json | Where-Object { $_.Name -eq 'banana' }

发生以下情况:

  1. ConvertFrom-Json returns 对象数组(对象本身)。由于这是第一个(最后也是唯一的)“完成”对象 ConvertFrom-Json return,它作为一个整体传递到管道中。请记住,一个 cmdlet 通常可以 return 多个对象数组。

  2. 因此,Where-Object 在这种情况下只接收一个对象(具有三个元素的整个数组)。 $_ 然后引用整个数组,而不是每个元素。因此,$_.Name并不是return一个元素的名称,而是所有元素名称的列表。此外,在这种情况下,术语 $_.Name -eq 'banana' 不是布尔表达式,而是经过过滤的元素名称列表(该列表仅包含 'banana')。只要列表不为空,它就会被 Where-Object 评估为 $true ,因此您的整个数组(一个对象,具有三个元素)将进一步通过管道传输(在您的情况下打印)。所以,它并不是你假设的return三个对象,而是一个对象,包含三个对象。

你的另一行对比:

($data | ConvertFrom-Json) | Where-Object { $_.Name -eq 'banana' }

好吧,简而言之,做你期望它做的事。为什么?因为圆括号破坏了管道。由于括号,括号内的所有内容都将被完全评估,然后再进一步传输。在你的括号被评估之后,有一个数组,它将被进一步传输。整个数组将逐个元素地通过管道传输。所以在这种情况下,Where-Object 收到三个单个对象,正如您所期望的那样。


另一个很好的例子是:

您不能覆盖您当前正在阅读的文件:

Get-Content test.txt | Set-Content test.txt

但是你可以在读完后覆盖一个文件:

(Get-Content test.txt) | Set-Content test.txt