我试图弄清楚是什么决定了一个值是否从PowerShell函数返回,我遇到了一些奇怪的事情。about_return文档说:
在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
现在, Package 赋值语句会导致它被返回,而 Package $n++
会导致它不被返回。
总之,我只想知道一种简单的方法,可以查看函数中的一行代码,并确定它是否会导致返回值。
1条答案
按热度按时间bkhjykvo1#
本节讨论示例函数中的特定语句。
背景信息见底部。
$n = 1
和$n++
是 * 赋值 *,因此 * 不 * 产生输出。$n
是一个 * 表达式 *,其值 * 是 * 输出$null
-同上,但即使它是 output,默认情况下也不会 display($n++)
-由于(...)
中的封装-将 assignment 转换为 expression,因此 does 也输出赋值。(++$n)
[System.Console]::WriteLine("Hello")
* 直接打印到控制台 *,这 * 绕过 * PowerShell的输出流系统。PowerShell的输出(“返回值”)行为:
向iRon表示感谢。
PowerShell遵循传统shell的模型,围绕 * 流*组织-请参阅概念about_Redirection帮助主题,了解PowerShell支持的所有6个流的概述。
也就是说,脚本或函数中的任何语句-因此可能有 * 多个 * 语句-都可以写入任何输出流。
primary 输出流是success 输出流**(编号为
1
),用于传输 data*,only it 默认情况下通过pipeline发送,因此默认情况下只有 it 被捕获到变量中,被抑制或重定向到文件。写入成功输出流有两种方式,即产生 data output:
显式地,使用*
Write-Output
调用-尽管很少需要**。**通常 * 隐式 ,即不捕获、抑制或重定向表达式、命令或语言语句产生的输出。
换句话说:*任何 command(如
Get-ChildItem *.txt
)或 expression(如1 + 2
或(42).ToString('x')
),甚至 language statement作为一个整体 (如foreach ($i in 0..2) { 1 + $i }
)的输出,默认都会发送到success输出流。与传统的编程语言不同,
return
* 不需要 * 产生输出-事实上,它的主要目的是 * 独立地 * 退出封闭作用域的任何输出,尽管作为一种*语法便利你可以 * 合并 * 这两个方面:return <command-or-expression>
实际上与下面的 two 语句相同,第一个语句(可能)产生输出,第二个语句退出作用域:<command-or-expression>; return
这种 * 隐式输出行为 * 很方便,并且允许简洁,富有表现力的代码,但也可能是一个陷阱:很容易 * 意外地 * 产生输出-通常来自不需要返回值的.NET方法(参见this question的示例)。
iRon的GitHub feature request #15781讨论了解决此问题的一种可能方法:引入选择加入严格模式,仅允许使用 explicit output语句(
Write-Output
、return
)以产生输出。This answer显示了可用于当前可用功能的故障排除技术。
对于assignments-例如
$n = 1; $n += 1; ++$n; $n--
:默认情况下,它们不 * 产生输出。
hybrid case是多重赋值的链式形式,例如
$a = $b = 1
,它将1
赋值给 both 变量:statement-internally 传递赋值,但语句 * 作为一个整体 * 没有输出。但是,作为 * 选择加入 *,您可以让它们通过 * 通过
(...)
(分组操作符)传递所分配的值;例如($n = 1)
将1
赋值给变量$n
, 输出1
,这允许它参与更大的表达式,例如($n = 1) -gt 0
请注意,相关的
$(...)
(子表达式运算符)和@(...)
(数组子表达式运算符)没有这种效果-它们 Package 了一个或多个完整的语句,而不会影响所包含语句的内在输出行为;例如,$($n = 1)
不产生输出,因为$n = 1
本身不产生输出;但是,$(($n = 1))
* 会 *,因为($n = 1)
本身会。*至于输出 * 枚举行为 :
默认情况下,PowerShell * 会枚举正在输出的 * collections,以 streaming 输出的精神:也就是说,它将集合的 * 元素 * 发送到管道, 一个接一个**。
在极少数情况下,你确实需要输出一个集合 * 作为一个整体 * -通常应该避免,以免混淆参与管道的其他命令,这些命令通常需要 * 逐对象 * 输入-你有两个选择:
, $collection
(sic;使用辅助的单元素 Package 器数组)更明确,但效率较低:
Write-Output -NoEnumerate $collection
有关详细信息,请参见this answer。
对于输出
$null
:$null
* 是 * 到管道的输出,但默认情况下 * 不显示。$null
本身不产生 * 可见 * 输出,但是下面的代码返回
$true
,说明值 was sent:$null
“值,用于表示命令的 lack of output,技术上表示为[System.Management.Automation.Internal.AutomationNull]::Value
singleton。在 expression 上下文中,此值与$null
相同,但是在 pipeline 中,它的行为就像一个 enumerable without elements,因此通过pipeline不发送任何东西**-有关更多信息,请参阅this answer。至于 * 抑制 (丢弃)不需要的输出/ * 重定向到文件:
*抑制语句的 success 输出的最佳通用方法是assign to
$null
($null = ...
);例如:$null
作为重定向目标,但类似地适用于将输出重定向到 file,通过指定文件名或路径作为目标。>$null
前面加上其number;例如3>$null
**抑制警告流输出。*>$null
**。至于 * 合并 * 输出流:
*只有 success 输出流(流号
1
)才能合并到。2>&1
和/或3>&1
),或者合并 * 所有 (其他):*>&1
2
)是[System.Management.Automation.ErrorRecord]
示例-有关更多信息,请参见this answer。关于 * 绕过 * PowerShell的流系统:
Out-Host
和[Console]::WriteLine()
调用 * 绕过 * PowerShell的输出流,直接写入主机/控制台(终端)。( 主机 * 是任何托管PowerShell引擎的环境,* 通常 *,但不一定是控制台(终端);其他主机的示例是PowerShell SDK和PowerShell中使用的主机remoting)。***
Write-Host
***以前 * 无条件绕过PowerShell的输出流,默认情况下 * 仍然会转到主机 *,但是-自PowerShell版本5以来-通过 * 信息流 *(流编号6
)路由其输出,其中它 * 可以 * 按需捕获/重定向-请参阅this answer了解更多信息。*输出 * 格式 :
Out-File
或其有效别名时,重定向运算符>
和>>
Export-Csv
或ConvertTo-Csv
)或JSON(使用ConvertTo-Json
)。至于 * 外界 * 如何看待PowerShell的输出流:
2>
)* 确实 * 将PowerShell的 * 错误流 *(仅)发送到该重定向目标。有关详细信息,请参阅this answer的底部部分。[1]请注意,PowerShell没有 input 流的概念,因此也 * 不 * 支持与其他shell相似的stdin重定向操作符
<
。(仅)* 通过管道 *.为了接收 * 来自外界 * 的数据,通过PowerShell CLI的stdin流,必须使用自动变量$input
-参见this answer。[2]使用
>
(或>>
)重定向到文件有效地在幕后使用Out-File
cmdlet,因此 * 它的 * 默认字符编码为“Unicode”(UTF-16 LE),以及 PowerShell中无BOM的UTF-8(Core)7+。但是,在PowerShell版本5.1及更高版本中,您可以通过$PSDefaultParameterValues
首选项变量控制此编码-请参阅this answer。