解释奇怪的函数调用处理/结果

时间:2019-04-04 19:25:05

标签: powershell

有人可以解释为什么/为什么这样的代码:

function DoIt($one, $two) 
{
    "`$one is $one"
    "`$two is $two"
    return $one * $two
}
$sum = DoIt 5 4
$sum

完全按预期/预期的方式工作-即,它将产生以下输出:

$one is 5
$two is 4
20

但是,此(看似几乎相同)的代码:

function DoIt($one, $two) 
{
    "`$one is $one"
    "`$two is $two"
    return $one * $two
}
$sum = DoIt 5 4
"`$sum is $sum"

通过产生以下输出来弯曲我的大脑并破坏我对现实的理解:

$sum is $one is 5 $two is 4 20

4 个答案:

答案 0 :(得分:2)

出现异常行为的原因是您正在污染输出流。 PowerShell没有像编译程序一样显式地具有纯“返回”值,而是依靠流在函数之间传递数据。有关更多信息,请参见about_Return,以及一个说明这种行为的示例。

基本上,$sum变量获取函数的所有输出,正如@TheMadTechnician所说,根据其使用方式,其输出有所不同。

PowerShell中返回值的“正确”方法通常不是使用return,而是使用Write-Output来明确定义要输出到的流。第二件事是在向主机控制台写入消息时必须使用Write-Host,否则它也会在输出流中返回。

function DoIt($one, $two) 
{
    Write-Host "`$one is $one"
    Write-Host "`$two is $two"
    Write-Output ($one * $two)
}
$sum = DoIt 5 4
"`$sum is $sum"

$one is 5
$two is 4
$sum is 20

答案 1 :(得分:1)

这是由于您如何输出数组,而与该函数无关。您可以使用以下方法复制相同的行为:

$sum='$one is 5','$two is 4',20
"`$sum is $sum"

将变量用双引号引起来将进行字符串扩展,该扩展将对数组中的每个项目执行.ToString()方法,然后将其与空格连接以将数组转换为字符串。

答案 2 :(得分:1)

现有的答案中有很多有用的信息,但让我尝试将所有信息汇总在一起:

function DoIt($one, $two) 
{
    "`$one is $one"
    "`$two is $two"
    return $one * $two
}

生成三个输出(“返回值”)到PowerShell的成功[输出]流(请参见about_Redirection),这是评估结果之:

  • 可扩展字符串"`$one is $one"
  • 可扩展字符串"`$two is $two"
  • 表达式$one * $two

可扩展字符串是隐式输出的-由于生成的结果既未捕获,也未发送到另一命令,也未重定向。

类似地,return中的return $one * $two只是用于以下目的的语法糖

$one * $two # implicit output
return      # control flow: exit the scope

注意:

  • return在这里是不必要的,而且从来没有是必需的。

  • 可以使用Write-Output 代替隐式输出:

    • 仅当您希望通过其-NoEnumerate开关将集合作为单个对象输出时,它才有用。
    • 否则,它不仅不必要冗长,还会使事情变慢。
  • 如果要从功能打印状态信息而又不污染输出流,则有多种选择:

    • Write-Host打印到主机的显示屏(如果您在控制台窗口中运行,则为控制台);在PSv4中,无法捕获或抑制此类输出;在PSv5 +中,Write-Host现在写入信息流(编号6),这意味着6>可用于重定向/抑制输出。
    • Write-Verbose用于选择加入的详细输出,您可以通过将首选项变量$VerbosePreference设置为'Continue'或使用-Verbose开关来激活它。
    • Write-Debug用于通过$DebugPreference-Debug选择接受调试输出,但是请注意,在后一种情况下,每当{{ 1}}语句。

因为这些输出是在变量 -Write-Debug中捕获的,并且有个以上的输出,所以它们< strong>隐式收集在数组 (类型为$sum = DoIt 5 4)中。

使用隐式输出将此变量的值打印到显示-[object[]]-枚举数组元素,它们分别在各自的行上打印:< / p>

$sum

通过对比,将隐式输出与可扩展字符串$one is 5 $two is 4 20 一起使用会生成单个输出字符串,其中引用了变量"`$sum is $sum",其中包含一个 array ,扩展如下:

  • 数组的每个元素都已字符串化

    • 松散地说,这就像在每个元素上调用$sum一样,但是必须注意,PowerShell选择了 culture-invariant 字符串表示形式(如果可用)-请参见{{3} }。
  • 结果与(很少使用的自动变量).ToString()的值作为分隔符连接在一起以形成单个字符串; $OFS默认为单个空格

因此,如果将数组元素$OFS$one is 5$two is 4连接在一起且它们之间只有一个空格,则会得到:

20

换句话说:以上等同于执行$one is 5 $two is 4 20

答案 3 :(得分:0)

玩了更多之后(使用从@ HAL9256中学到的信息),我发现问题实际上不是使用returnWrite-Output而是问题在于如何处理无命令的字符串在功能上。例如,这也很完美:

function DoIt($one, $two) 
{
    Write-Host "`$one is $one"
    Write-Host "`$two is $two"
    return ($one * $two)
}
$sum = DoIt 5 4
Write-Host "`$sum is $sum"

也就是说,它会产生以下预期输出:

$one is 5
$two is 4
$sum is 20

(而且,显然,没有显式括号的数学计算在return命令中也可以正常工作-与Write-Output命令中的命令不同。)

换句话说,我想函数中的静态字符串被视为您正在构建要返回的数组(就像@TheMadTechnician所指),而不是Write-Host命令的简写形式。生活和学习-“明确胜于隐性”。 :-)

当然,我仍然不明白为什么我的原始问题中两个代码块之间的最终输出不完全相同(对还是错)...但是,嘿,我的大脑已经一天累了。 :-P

再次感谢大家!