Powershell 函数 returns 标准输出? (旧标题:Powershell append to array of arrays appends stdout?)

Powershell function returns stdout? (Old title: Powershell append to array of arrays appends stdout?)

我从 this thread 那里学到了如何追加到数组的数组。但是,我观察到有史以来最奇怪的行为。它似乎也附加了标准输出!假设我想附加到一个数组数组,但我想在循环中回显调试消息。考虑以下功能。

function WTF {
  $Result = @()
  $A = 1,2
  $B = 11,22
  $A,$B | % {
    Write-Output "Appending..."
    $Result += , $_
  }
  return $Result
}

现在,如果您执行 $Thing = WTF,您可能会认为您得到一个 $Thing,它是两个数组的数组:$(1,2)$(11,22)。但事实并非如此。如果你在终端输入 $Thing,你实际上得到:

Appending...
Appending...
1
2
11
22

$Thing[0]是字符串“Appending...”,$Thing[1]是字符串“Appending...”,$Thing[2]是数组@(1,2),而$Thing[3]是数组@(11,22)。没关系,它们的顺序似乎很奇怪,这是我不想进入的一整套蠕虫,但为什么回显的字符串“Appending...”会附加到结果中???这太奇怪了。我如何阻止它这样做?

编辑

哦等等,经过进一步试验,以下更简单的函数可能更能说明问题:

function WTF {
  $Result = @()
  1,2 | % {
    Write-Output "LOL"
  }
  return $Result

现在,如果您执行 $Thing = WTF,那么 $Thing 将成为一个长度为 2 的数组,其中包含字符串“LOL”两次。显然,Powershell 循环和/或 Write-Output 有一些我不理解的基础知识。

另一个编辑!!!

事实上,以下更简单的函数可以做同样的事情:

function WTF {
  1,2 | % {
    Write-Output "LOL"
  }
}

也许我不应该使用 Write-Output,而应该使用 Write-InformationWrite-Debug

  • PowerShell 没有 return 值,它有一个成功输出流(模拟传统 shell 中的 stdout

    • PowerShell pipeline serves as the conduit for this stream, both when capturing command output in a variable and when sending it to another command via |, the pipeline operator
  • 任何语句 - 包括 多个 语句,可能在 循环 中 - 在函数或脚本中可以写入该流,通常 隐式 - 通过不捕获、抑制或重定向输出 - 或 显式 ,以及 Write-Output, although its use is rarely needed - see 以获取更多信息。

    • 输出被立即发送到成功输出流,因为它正在生成 - 甚至在脚本或函数退出之前。
  • return 存在 流量控制 独立 PowerShell 的输出行为;作为语法糖,您可能也可以使用它来写入输出流;也就是说,return $Result$Result; return 的语法糖,$Result 本身产生隐式输出,return 退出作用域。

  • 为了避免污染 success 输出流 - 仅用于 data 输出 - 带有状态消息,写入其他 可用输出流之一 - 请参阅概念性 about_Redirection 帮助主题。

    • Write-Verbose is a good choice, because it is silent by default, and can be activated on demand, either via $VerbosePreference = 'Continue', or, on a per-call basis, with the common -Verbose parameter, assuming the script or function is an advanced一个.

    • Write-Host,相比之下,无条件 将信息打印到主机(显示器),并允许控制格式,特别是着色。

  • 从脚本或函数枚举输出集合(数组)。也就是说,不是将集合本身作为一个整体发送,而是它的元素被一个一个地发送到PowerShell的管道, ,一个名为 streaming.

    的进程
    • PowerShell 命令通常需要流式输出,如果您输出一个集合 作为一个整体.

      ,则可能不会按预期运行
    • 当您确实想要将集合作为一个整体输出时(出于性能原因,有时可能是必需的),请将它们包装在瞬态辅助中。单元素数组,使用,的一元形式,array constructor operator, $Result

      • 概念上更清晰(但效率较低)的替代方法是使用 Write-Output -NoEnumerate
      • 有关详细信息,请参阅

因此,您函数的 PowerShell 惯用重构是:

function WTF {
  
  # Even though no parameters are declared,
  # these two lines are needed to activate support for the -Verbose switch,
  # which implicitly make the function an *advanced* one.
  # Even without it, you could still control verbose output via
  # the $VerbosePreference preference variable.
  [CmdletBinding()]
  param()

  $A = 1,2
  $B = 11,22
  
  # Use Write-Verbose for status information.
  # It is silent by default, but if you pass -Verbose
  # on invocation or set $VerbosePreference = 'Continue', you'll 
  # see the message.
  Write-Verbose 'Appending...'

  # Construct an array containing the input arrays as elements
  # and output it, which *enumerates* it, meaning that each
  # input array is output by itself, as a whole.
  # If you need to output the nested array *as a whole*, use
  #    , ($A, $B)
  $A, $B
}

示例调用:

PS> $nestedArray = WTF -Verbose
VERBOSE: Appending...

注:

  • 只有 success 输出(流 1)在变量 $nestedArray 中被捕获,而 verbose 输出(流 4)已传递到显示器。

  • $nestedArray 最终成为一个 数组 - 尽管 $A$B 实际上是流式传输 separately - 因为 PowerShell 自动 收集多个 数组中的流对象,它总是类型 [object[]].

  • 一个值得注意的陷阱是,如果只有 一个 输出对象,它会按原样分配,而不是包装在数组中。

    • 确保一个命令的输出是总是一个数组,即使是在单对象输出的情况下:

      • 您可以将命令放在@(...)中,array-subexpression operator

         $txtFiles = @(Get-ChildItem *.txt)
        
      • 在变量赋值的情况下,您还可以使用 类型约束 [array](实际上与 [object[]]):

         [array] $txtFiles = Get-ChildItem *.txt
        
      • 但是,请注意,如果给定的单个输出对象 本身 是一个集合(如前所述,这是不寻常的),则 extra array wrapper 由上述命令创建,如果该集合的类型不是数组,它将被转换为常规[object[]]数组。
        此外,如果 @(...) 应用于 强类型 数组(例如 [int[]] (1, 2),它实际上被枚举并重建为 [object[]] 数组;相比之下,[array] 类型约束(转换)按原样保留这样的数组。


关于您的具体观察和问题

I've learnt from this thread how to append to an array of arrays

虽然使用 += 以增量构建数组 方便 ,但它也 低效 ,因为 =111=]new 数组每次都必须构造 - 请参阅 for how to construct arrays efficiently, and 以了解如何使用可有效扩展的 list 类型作为替代。

Nevermind that they seem to be in a weird order, which is a whole other can of worms I don't want to get into

输出到管道 - 无论是隐式还是显式 Write-Output 都会 立即 发送到成功输出流。

因此,你的 Write-Output 输出首先出现 ,因为你直到后来才通过 return 输出 $Result 数组.

How do I stop it from doing that?

如前所述,不要将 Write-Output 用于 status 消息。