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

Dar*_*te1 5 powershell powershell-5.1

考虑以下代码:

$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' }
Run Code Online (Sandbox Code Playgroud)

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

我们在这里遗漏了一些明显的东西吗?

sta*_*tor 1

和:

$data | ConvertFrom-Json | Where-Object { $_.Name -eq 'banana' }
Run Code Online (Sandbox Code Playgroud)

发生以下情况:

  1. ConvertFrom-Json返回一个对象数组(它本身就是一个对象)。由于这是第一个(最终也是唯一一个)“完成”对象ConvertFrom-Json返回,因此它作为一个整体沿着管道传递。请记住,cmdlet 通常可以返回多个对象数组。

  2. 因此,Where-Object在这种情况下仅接收一个对象(包含三个元素的整个数组)。$_然后引用整个数组,而不是每个元素。因此,$_.Name不会返回一个元素的名称,而是返回所有元素名称的列表。此外,本例中的术语$_.Name -eq 'banana'不是布尔表达式,而是经过过滤的元素名称列表(该列表仅包含“banana”)。只要列表不为空,它就会被评估为$trueby Where-Object,因此您的整个数组(一个对象,包含三个元素)将被进一步传送(在您的情况下打印)。因此,它不会像您假设的那样返回三个对象,而是返回一个包含三个对象的对象。

相比之下,你的另一行:

($data | ConvertFrom-Json) | Where-Object { $_.Name -eq 'banana' }
Run Code Online (Sandbox Code Playgroud)

好吧,简而言之,做你期望它做的事情。为什么?因为圆括号破坏了管道。由于括号的存在,括号内的所有内容都将在进一步传输之前被完全评估。评估括号后,会出现一个数组,该数组将被进一步传送。整个数组将逐个元素进行管道传输。因此,在本例中,Where-Object如您所期望的那样,接收三个单个对象。


另一个很好的例子是这样的:

您无法覆盖当前正在读取的文件:

Get-Content test.txt | Set-Content test.txt
Run Code Online (Sandbox Code Playgroud)

但是您可以在读完文件后覆盖它:

(Get-Content test.txt) | Set-Content test.txt
Run Code Online (Sandbox Code Playgroud)