【问题标题】:What decides if a value is returned from a PowerShell function?什么决定是否从 PowerShell 函数返回值?
【发布时间】:2021-12-15 20:25:15
【问题描述】:

我试图弄清楚是什么决定了值是否从 PowerShell 函数返回,但我遇到了一些奇怪的问题。 about_return docs 说:

在 PowerShell 中,每个语句的结果都会作为输出返回,即使没有包含 Return 关键字的语句。

但这似乎掩盖了细节。如果我运行这个:

function My-Function {
    1
    [System.Console]::WriteLine("Hello")
    $null
    $true
    $false
    0
    2
}

运行它会返回一个数组(连同打印“Hello”):

1
True
False
0
2

这意味着$null 不会自动返回。然后我尝试递增,因为我在函数中使用它:

function My-Function {
    $n = 1
    $n
    $n++
    ($n++)
    -join @(1, 2, 3)
    (-join @(1, 2, 3))
}

返回:

1
2
123
123

所以,$n$n++ 已返回,但 ($n++) 没有返回?但是当与另一个运营商 (-join) 相比时,在每种情况下都是相同的。为什么$n++ 周围的括号会阻止它被返回,为什么其他运算符的行为不一样?这更加令人困惑,因为 = 运算符似乎以相反的方式工作:

function My-Function {
    ($n = 1)
    $n
    $n++
    ($n++)
}

返回:

1
1
2

现在包装赋值会导致它被返回,而包装 $n++ 会导致它不被返回。

总之,我只想知道一种直接的方法,可以查看函数中的一行代码,并确定它是否会导致返回值。

【问题讨论】:

  • 其实$n++没有返回,($n++)被返回了。
  • @SantiagoSquarzon 哇,不知何故,我设法混淆了前后递增。太尴尬了。
  • PS 的好处是我们可以在控制台上测试所有内容,我现在无法解释为什么但是用( ) 包装递增变量将强制它返回到控制台。当您执行$n = 0 时,($n++)0 将返回到标准输出,但 $n 将等于 1(不是您的代码的一部分,但将其作为示例)
  • 这叫做变量挤压。通常静默变量赋值也会发生同样的事情,它也会输出
  • @SantiagoSquarzon 我现在意识到$null 在打印时似乎没有出现在数组中,这让我感到困惑。如果我使用-join 打印出返回的数组以插入分隔符,是的,我可以看到它确实返回了,只是没有显示。感谢您的指导。

标签: function powershell powershell-7.0


【解决方案1】:

本节讨论示例函数中的特定语句。
有关背景信息,请参阅底部部分。

  • $n = 1$n++分配,因此产生输出。
  • $n 是一个表达式,它的值输出
  • $null - 同上,但即使它是输出,默认情况下它也不会显示
  • ($n++) - 由于 (...) 中的封闭 - 将 assignment 转换为 expression,因此 确实 输出分配的值(也)。
    • 但是,因为您使用了 post-increment 形式的赋值,所以输出的是 old 值,而不是 now incremented 值;要首先递增(预递增)和 then 输出,请使用 (++$n)
  • [System.Console]::WriteLine("Hello")直接打印到控制台,这绕过PowerShell的输出流系统。
    • 这意味着 您无法从 PowerShell 内部捕获或重定向此类输出(请参阅下一节)。

PowerShell 的输出(“返回值”)行为:

iRon 致敬以寻求帮助。

PowerShell 遵循传统 shell 的模型,围绕 组织 - 请参阅概念性的 about_Redirection 帮助主题以了解PowerShell 支持的所有 6 个流。[1]

也就是说,脚本或函数中的任何语句(因此可能有多个语句)都可以写入任何输出流

primary 输出流,用于传送数据,是成功 输出流(编号为1),默认只有it通过pipeline发送,因此默认只有it em> 在变量中被捕获、抑制或重定向到文件。

两种方式写入成功输出流,即产生数据输出

  • 显式地,带有 Write-Output 调用 - 尽管这是很少需要

  • 通常隐式,即不捕获、抑制或重定向语句产生的输出

    • 换句话说:任何命令(例如Get-ChildItem *.txt)或表达式(例如1 + 2)的输出都会发送到成功输出流默认

    • 与传统编程语言不同,return不需要产生输出 - 事实上,它的主要目的是独立地退出封闭范围 范围产生的任何输出,但为了语法便利,您可以结合这两个方面:

      • return <command-or-expression> 实际上与以下 两个 语句相同,其中第一个(可能)产生输出,第二个退出范围:<command-or-expression>; return
    • 这种隐式输出行为很方便,并且允许简洁、富有表现力的代码,但也可能是一个陷阱很容易意外 产生输出 - 通常来自不需要返回值的 .NET 方法(参见 this question 的示例)。

      • iRon 的GitHub feature request #15781 讨论了解决此问题的一种潜在方法:引入仅允许使用显式 输出语句(Write-Outputreturn)的选择性加入严格模式,以便产生输出。

      • This answer 展示了故障排除技术,您可以将其用于当前可用的功能。

至于assignments - 例如$n = 1; $n += 1; ++$n; $n--:

  • 默认情况下,它们产生输出
    • hybrid case 是multi-assignment 的链接形式,例如$a = $b = 1,将1 赋值给两个变量:语句-在内部传递赋值值,但语句作为一个整体没有输出。
  • 但是,作为选择加入,您可以让他们通过(...) 传递通过分配的值,即grouping operator;例如($n = 1) 都将1 分配给变量$n 输出1,这允许它参与更大的表达式,例如($n = 1) -gt 0
    • 请注意,相关的 $(...) (subexpression operator) 和 @(...) (array-subexpression operator) 确实具有这种效果 - 它们包含一个或多个整个语句,不影响封闭语句的内在输出行为;例如$($n = 1) 不会 产生输出,因为$n = 1 本身不会产生输出;但是,$(($n = 1)) 可以,因为 ($n = 1) 本身就可以。

至于输出枚举行为

  • 默认情况下,PowerShell 枚举正在输出的集合,本着输出的精神:也就是说,它发送一个集合的元素到管道,一个接一个

  • 在极少数情况下,您确实需要输出一个集合作为一个整体 - 通常应该避免这样做,以免混淆参与管道的其他命令,这些命令通常确实期望 object-by-object 输入 - 你有两个选择:

    • , $collection(原文如此;使用辅助单元素包装器数组)
    • 更明确,但效率较低:Write-Output -NoEnumerate $collection
    • 请参阅this answer 了解更多信息。

至于输出$null

  • $null 输出到管道,但默认情况下不显示。 p>

    • $null 本身不会产生 可见 输出,

    • 但以下返回$true,表明值已发送

      $null | ForEach-Object { $null -eq $_ } # -> $true
      
  • 请注意,PowerShell 还有一个“数组值$null”值,它用于表示命令的缺少输出,技术上表示为[System.Management.Automation.Internal.AutomationNull]::Value 单身人士。 表达式 上下文中,此值被视为与 $null 相同,但 管道 中它的行为类似于 没有元素的可枚举,因此通过管道发送任何内容 - 请参阅this answer 了解更多信息。

至于抑制(丢弃)不需要的输出/重定向到文件

  • 抑制语句的成功输出的最佳通用方法是分配给$null ($null = ...);例如:

     # .Add() outputs a value, which is usually unwanted.
     $null = ($list = [System.Collections.ArrayList]::new()).Add('hi')
    
  • 注意:以下讨论输出抑制,通过$null 作为重定向目标,但类似地适用于将输出重定向到文件,通过指定文件名或路径作为目标。[2]

    • 选择性地抑制不同输出流,请在>$null 前加上number;例如3>$null 禁止警告流输出。

    • 抑制来自所有的输出,在外部程序同时涵盖stdout和stderr的情况下,请使用重定向@ 987654408@.

至于合并输出流

  • 只有成功输出流(流号1)可以合并到
  • 您可以有选择地将一个或多个输出流合并到其中(例如2>&1和/或3>&1),或合并所有(其他):*>&1
  • 在生成的合并成功输出流中,您仍然可以通过检查给定对象的类型来识别给定对象来自哪个(非成功)流;例如,error 流对象(流 2)是 [System.Management.Automation.ErrorRecord] 实例 - 请参阅 this answer 了解更多信息。

至于绕过 PowerShell 的流系统:

  • Out-Host[Console]::WriteLine() 调用绕过 PowerShell 的输出流 并直接写入主机/控制台(终端)。 (host 是托管 PowerShell 引擎的任何环境,通常是,但不一定是控制台(终端);其他主机的示例是 PowerShell SDK 和PowerShell 中使用的主机remoting)。

    • 因此,无法从 PowerShell 内部捕获、抑制或重定向它们的输出
  • Write-Host 以前无条件绕过了 PowerShell 的输出流,并且仍然默认发送到主机,但是 - 从 PowerShell 版本 5 开始 - 路由它通过信息流(流号6)输出,可以按需捕获/重定向 - 请参阅this answer了解更多信息。

至于输出如何格式化

  • 如果未捕获、抑制或重定向输出,则默认将其发送到主机(控制台),并根据 PowerShell 丰富且可自定义的 for-display 输出格式系统进行呈现。请参阅 this answer 以获得简要概述。

  • 请注意,生成的表示是为人类观察者设计的,而不是程序处理。虽然 PowerShell 在实际 数据 其表示 之间保持了明确的分离,但 警告 是你最终会在以下场景中仅使用用于显示的字符串表示:

    • 当您使用Out-File 或其有效别名时,重定向运算符>>>
    • 当您(隐式)将输出发送到外部世界(外部调用者的标准输出流 - 见下文)。
    • 在这两种情况下,如果需要以后进行编程处理,则必须将数据转换为 结构化 文本格式,例如 CSV(Export-CsvConvertTo-Csv)或 JSON(使用 @987654351 @)。

至于外界如何看待PowerShell的输出流:

  • 操作系统级别的IPC(进程间通信)只知道两个输出流:stdout(标准输出)和stderr(标准输出)错误),这会强制 PowerShell 将其 6 个输出流映射到这两个上,以便将流输出返回给外部调用者。

  • 虽然将 PowerShell 的成功输出流映射到 stdout 并将 所有其他流 映射到 stderr 是有意义的,但不幸的是,所有流都是通过 stdout 报告的 默认从 PowerShell 7.2 开始 - 尽管在调用过程中选择性地重定向 stderr(通常使用 2>确实发送 PowerShell 的 错误流(仅)到该重定向目标。有关详细信息,请参阅this answer 的底部。

  • 另请注意,从 7.2 版开始,PowerShell 仅通过 text(字符串)外部调用者 以及 进行通信从 PowerShell 会话中调用的外部程序,这意味着可能会出现字符编码问题 - 请参阅 this answer 了解更多信息。


[1] 请注意,PowerShell 本身没有 input 流的概念,因此 not 也支持 stdin 重定向运算符 < 熟悉从其他贝壳。相反,命令接收流输入(仅)通过管道。为了从外部世界接收数据,通过 PowerShell CLI 的标准输入流,必须使用 automatic $input variable - 请参阅 this answer

[2] 使用>(或>>)重定向到文件在后台有效地使用Out-File cmdlet,因此默认字符编码,即在 Windows PowerShell 中是“Unicode” (UTF-16LE),在 PowerShell (Core) 7+ 中是无 BOM 的 UTF-8。但是,在 PowerShell 5.1 及更高版本中,您可以通过$PSDefaultParameterValues preference variable 控制此编码 - 请参阅this answer

【讨论】:

    猜你喜欢
    • 2016-09-12
    • 2016-05-26
    • 1970-01-01
    • 2014-05-15
    • 1970-01-01
    • 1970-01-01
    • 2019-01-08
    • 1970-01-01
    • 2022-12-31
    相关资源
    最近更新 更多