gpt4 book ai didi

arrays - PowerShell 的 -f 运算符的 RHS *究竟*如何工作?

转载 作者:行者123 更新时间:2023-12-01 10:14:57 28 4
gpt4 key购买 nike

Last time I got confused顺便说一下PowerShell急切地展开集合,基思总结了它的启发式如下:

Putting the results (an array) within a grouping expression (or subexpression e.g. $()) makes it eligible again for unrolling.



我已经把这个建议牢记在心,但仍然发现自己无法解释一些深奥的东西。特别是, Format 运算符似乎不遵守规则。
$lhs = "{0} {1}"

filter Identity { $_ }
filter Square { ($_, $_) }
filter Wrap { (,$_) }
filter SquareAndWrap { (,($_, $_)) }

$rhs = "a" | Square
# 1. all succeed
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

$rhs = "a" | Square | Wrap
# 2. all succeed
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

$rhs = "a" | SquareAndWrap
# 3. all succeed
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

$rhs = "a", "b" | SquareAndWrap
# 4. all succeed by coercing the inner array to the string "System.Object[]"
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

"a" | Square | % {
# 5. all fail
$lhs -f $_
$lhs -f ($_)
$lhs -f @($_)
$lhs -f $($_)
}

"a", "b" | Square | % {
# 6. all fail
$lhs -f $_
$lhs -f ($_)
$lhs -f @($_)
$lhs -f $($_)
}

"a" | Square | Wrap | % {
# 7. all fail
$lhs -f $_
$lhs -f ($_)
$lhs -f @($_)
$lhs -f $($_)
}

"a", "b" | Square | Wrap | % {
# 8. all fail
$lhs -f $_
$lhs -f ($_)
$lhs -f @($_)
$lhs -f $($_)
}

"a" | SquareAndWrap | % {
# 9. only @() and $() succeed
$lhs -f $_
$lhs -f ($_)
$lhs -f @($_)
$lhs -f $($_)
}

"a", "b" | SquareAndWrap | % {
# 10. only $() succeeds
$lhs -f $_
$lhs -f ($_)
$lhs -f @($_)
$lhs -f $($_)
}

应用我们在上一个问题中看到的相同模式,很明显为什么像 #1 和 #5 这样的情况表现不同:管道运算符向脚本引擎发出信号以展开另一个级别,而赋值运算符没有。换句话说,位于两个 | 之间的所有内容都被视为一个分组表达式,就好像它在 () 中一样。
# all of these output 2
("a" | Square).count # explicitly grouped
("a" | Square | measure).count # grouped by pipes
("a" | Square | Identity).count # pipe + ()
("a" | Square | Identity | measure).count # pipe + pipe

出于同样的原因,案例#7 与#5 相比没有任何改进。任何添加额外 的尝试包裹 将立即被多余的管道颠覆。同上 #8 与 #6。有点令人沮丧,但我完全同意这一点。

剩下的问题:
  • 为什么案例#3 没有遭受与#4 相同的命运? $rhs 应该保存嵌套数组 (,("a", "a")) 但它的外层正在展开……某处……
  • #9-10 中的各种分组运算符是怎么回事?为什么他们的行为如此不规律,为什么需要他们?
  • 为什么 #10 的失败不会像 #4 那样优雅地降级?
  • 最佳答案

    嗯,这肯定有一个错误。 (实际上,我昨天刚刚写了 a page on the PoshCode Wiki 关于它,并且有一个 bug on connect )。

    先回答,后面有更多问题:

    使用 -f 从数组中获得一致的行为字符串格式,您需要 100% 确定它们是 PSObject。我的建议是在分配它们时这样做。它应该由 PowerShell 自动完成,但由于某种原因,直到您访问属性或其他东西(如 wiki pagebug 中所述)才完成。例如(<##> 是我的提示):

    <##> $a = 1,2,3
    <##> "$a"
    1 2 3

    <##> $OFS = "-" # Set the Output field separator
    <##> "$a"
    1-2-3

    <##> "{0}" -f $a
    1

    <##> $a.Length
    3

    <##> "{0}" -f $a
    1-2-3

    # You can enforce correct behavior by casting:
    <##> [PSObject]$b = 1,2,3
    <##> "{0}" -f $a
    1-2-3

    请注意,当您这样做时,它们在传递给 -f 时不会展开,而是会正确输出——就像您将变量直接放在字符串中时一样。

    为什么案例#3 没有遭受与#4 相同的命运? $rhs 应该保存嵌套数组 (,("a", "a")) 但它的外层正在展开......某处......

    答案的简单版本是 #3 和 #4 都在展开。不同的是,在 4 中,内部内容是一个数组(即使在展开外部数组之后):
    $rhs = "a" | SquareAndWrap
    $rhs[0].GetType() # String

    $rhs = "a","b" | SquareAndWrap
    $rhs[0].GetType() # Object[]

    #9-10 中的各种分组运算符是怎么回事?为什么他们的行为如此不规律,为什么需要他们?

    正如我之前所说,数组应该算作格式的单个参数,并且应该使用 PowerShell 的字符串格式规则(即:用 $OFS 分隔)输出,就像将 $_ 直接放入字符串中一样。 . 因此,当 PowerShell 正常运行时, $lhs -f $rhs如果 $lhs 包含两个占位符,将失败。

    当然,我们已经观察到其中有一个错误。

    但是,我没有看到任何不稳定的情况:据我所知,@() 和 $() 对于 9 和 10 的工作方式相同(实际上,主要区别是由 ForEach 展开顶级数组的方式引起的:
    > $rhs = "a", "b" | SquareAndWrap
    > $rhs | % { $lhs -f @($_); " hi " }
    a a
    hi
    b b
    hi

    > $rhs | % { $lhs -f $($_); " hi " }
    a a
    hi
    b b
    hi

    # Is the same as:
    > [String]::Format( "{0} {1}", $rhs[0] ); " hi "
    a a
    hi

    > [String]::Format( "{0} {1}", $rhs[1] ); " hi "
    b b
    hi

    所以你看到的错误是@() 或 $() 将导致数组作为 [object[]] 传递给字符串格式调用,而不是作为具有特殊 to-string 值的 PSObject。

    为什么 #10 的失败不会像 #4 那样优雅地降级?

    这基本上是相同的错误,但表现形式不同。数组不应该在 PowerShell 中以“System.Object[]”的形式出现,除非您手动调用它们的原生 .ToString()方法,或者直接将它们传递给 String.Format() ...他们在 #4 中这样做的原因是错误:PowerShell 在将它们传递给 String.Format 调用之前未能将它们扩展为 PSOjbects。

    如果您在传入数组之前访问该数组的属性,或者将其转换为 PSObject,就像在我的原始示例中一样,您可以看到这一点。从技术上讲,#10 中的错误是正确的输出:您只将一个东西(一个数组)传递给 string.format,而它需要两个东西。如果您将 $lhs 更改为“{0}”,您会看到使用 $OFS 格式化的数组

    不过我想知道,你是哪种行为 喜欢 你认为哪个是 正确 ,考虑我的第一个例子?我认为 $OFS 分隔的输出是正确的,而不是像 @(wrap) 它那样展开数组,或者将其转换为 [object[]] (顺便提一下,如果将它转换为 [int[ ]] 是一种不同的错误行为):
    > "{0}" -f [object[]]$a
    1

    > "{0}, {1}" -f [object[]]$a # just to be clear...
    1,2

    > "{0}, {1}" -f [object[]]$a, "two" # to demonstrate inconsistency
    System.Object[],two

    > "{0}" -f [int[]]$a
    System.Int32[]

    我敢肯定很多脚本是在不知不觉中利用这个错误编写的,但我仍然很清楚,在只是为了清楚的例子中发生的展开并不是正确的行为,而是因为,在调用(在 PowerShell 的核心内)到 .Net String.Format( "{0}", a ) ... $aobject[]这是 String.Format 所期望的,因为它是 Params 参数...

    我认为必须解决这个问题。如果想要保持展开数组的“功能”,应该使用@splatting 操作符来完成,对吧?

    关于arrays - PowerShell 的 -f 运算符的 RHS *究竟*如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1872146/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com