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

Powershell 解释奇怪的函数调用处理/结果,powershell,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 $sum is $one is 5 $two is 4 20 但是,这似乎是相同的代码: function DoIt($one, $two

有人能解释一下这个代码是如何产生的吗

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
$sum is $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"
通过产生以下输出,弯曲我的大脑,打破我对现实的理解:

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

这是由于输出数组的方式造成的,与函数无关。您可以通过以下方式复制相同的行为:

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

将变量放在双引号中执行字符串扩展,该扩展对数组中的每个项执行.ToString方法,然后将它们与一个空格连接,以将数组转换为字符串。

这是由于您输出数组的方式,与函数无关。您可以通过以下方式复制相同的行为:

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

将变量置于双引号中会执行字符串扩展,该扩展会对数组中的每个项执行.ToString方法,然后将它们与一个空格连接,以将数组转换为字符串。

这种奇怪行为的原因是您正在污染输出流。PowerShell不像编译程序那样显式地具有纯返回值,而是依赖流在函数之间传递数据。有关更多信息,以及说明此行为的示例,请参见

基本上,$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

这种奇怪行为的原因是您正在污染输出流。PowerShell不像编译程序那样显式地具有纯返回值,而是依赖流在函数之间传递数据。有关更多信息,以及说明此行为的示例,请参见

基本上,$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

在更多地使用从@HAL9256学到的信息之后,我发现问题实际上并不在于使用返回与写入输出,而是在于函数中如何处理无命令字符串。例如,这也非常有效:

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


再次感谢大家

在更多地使用我从@HAL9256学到的信息之后,我发现问题实际上并不在于使用return vs Write 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


再次感谢大家

现有的答案中有很多好的信息,但让我试着把它们综合起来:

function DoIt($one, $two) 
{
    "`$one is $one"
    "`$two is $two"
    return $one * $two
}
为PowerShell的成功[output]流生成三个输出返回值,这些值是以下各项的评估结果:

可扩展字符串“$one是$one” 可扩展字符串`$two是$two 表达式$1*$2 可扩展字符串是隐式输出的,因为生成的结果既不是捕获的,也不是发送给另一个 命令,也没有重定向

类似地,return-in-return$one*$two只是语法糖:

注:

这里不需要退货,也不需要退货

虽然可以使用写输出代替隐式输出:

只有当您希望通过-NoEnumerate开关将集合作为单个对象输出时,它才有用。 否则,这不仅是不必要的冗长,而且会减慢速度。 如果要打印函数中的状态信息而不污染输出流,则有以下几种选择:

如果您在控制台窗口中运行,则将主机打印内容写入控制台的主机显示屏;在PSv4-中,这样的输出既不能被捕获也不能被抑制;在PSv5+中,Write Host现在写入到编号为6的信息流,这意味着6>可用于重定向/抑制输出。 Write Verbose用于opt-in Verbose输出,您可以通过将首选项变量$VerbosePreference设置为“Continue”或使用-Verbose开关来激活该输出。 Write Debug用于通过$DebugPreference或-Debug选择调试输出,但请注意,在后一种情况下,每当遇到Write Debug语句时,都会显示一个提示。 由于这些输出是在变量$sum=DoIt 5 4中捕获的,并且有多个输出,因此它们隐式收集在类型为[object[]的数组中

使用隐式输出将此变量的值打印到显示器-$sum-枚举数组元素,数组元素在各自的行上打印它们:

$one is 5
$two is 4
20
相反,将隐式输出与可展开字符串“$sum is$sum”一起使用会产生单个输出字符串,其中对变量$sum的引用(包含数组)展开如下:

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

粗略地说,这类似于在每个元素上调用.ToString,但需要注意的是,PowerShell选择区域性不变的字符串表示形式(如果可用)-请参阅。 结果与很少使用的自动变量$OFS的值合并作为分隔符,形成一个字符串$OFS默认为单个空间

因此,如果将数组元素$1为5、$2为4、20之间用一个空格连接起来,则得到:

$one is 5 $two is 4 20

换言之:上面的语句相当于执行“$1是5”,“$2是4”,“20'-join”

现有的答案中有很多好的信息,但让我试着把它们放在一起:

function DoIt($one, $two) 
{
    "`$one is $one"
    "`$two is $two"
    return $one * $two
}
为PowerShell的成功[output]流生成三个输出返回值,这些值是以下各项的评估结果:

可扩展字符串“$one是$one” 可扩展字符串`$two是$two 表达式$1*$2 可扩展字符串是隐式输出的,因为生成的结果既不会被捕获,也不会被发送到另一个命令,也不会被重定向

类似地,return-in-return$one*$two只是语法糖:

注:

这里不需要退货,也不需要退货

虽然可以使用写输出代替隐式输出:

只有当您希望通过-NoEnumerate开关将集合作为单个对象输出时,它才有用。 否则,这不仅是不必要的冗长,而且会减慢速度。 如果要打印函数中的状态信息而不污染输出流,则有以下几种选择:

如果您在控制台窗口中运行,则将主机打印内容写入控制台的主机显示屏;在PSv4-中,这样的输出既不能被捕获也不能被抑制;在PSv5+中,Write Host现在写入到编号为6的信息流,这意味着6>可用于重定向/抑制输出。 Write Verbose用于opt-in Verbose输出,您可以通过将首选项变量$VerbosePreference设置为“Continue”或使用-Verbose开关来激活该输出。 Write Debug用于通过$DebugPreference或-Debug选择调试输出,但请注意,在后一种情况下,每当遇到Write Debug语句时,都会显示一个提示。 由于这些输出是在变量$sum=DoIt 5 4中捕获的,并且有多个输出,因此它们隐式收集在类型为[object[]的数组中

使用隐式输出将此变量的值打印到显示器-$sum-枚举数组元素,数组元素在各自的行上打印它们:

$one is 5
$two is 4
20
相反,将隐式输出与可展开字符串“$sum is$sum”一起使用会产生单个输出字符串,其中对变量$sum的引用(包含数组)展开如下:

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

粗略地说,这类似于在每个元素上调用.ToString,但需要注意的是,PowerShell选择区域性不变的字符串表示形式(如果可用)-请参阅。 结果与很少使用的自动变量$OFS的值合并作为分隔符,形成一个字符串$OFS默认为单个空间

因此,如果将数组元素$1为5、$2为4、20之间用一个空格连接起来,则得到:

$one is 5 $two is 4 20
换一种说法,那就是
上面的代码相当于执行“$1是5”,“$2是4”,“20'-加入”

谢谢@HAL9256-这很有帮助!然而,正如你写的,我得到了$sum是5*4。数学计算似乎并没有像我预期的那样在写输出命令中进行。但是,这可以通过将一行代码分成两行来轻松解决:$retval=$one*$two和Write Output$retvalArgh,@RichBayless是的,你是对的。简单地将一些括号括在$one*$two周围,会导致它计算表达式,而不是输出原始字符串输出。我已经编辑了答案以更正它。有趣的是,我从我之前的测试中复制了这个表达式,在这个测试中,我没有写入输出,而是返回$1*$2,不带括号,它成功了;-啊,所有这些测试边缘案例!除非另有规定,否则所有输出都发送到标准输出,因此返回和写入输出是冗余的。我建议使用[cmdletbinding]和Write-Verbose,而不是使用Write-Host。谢谢@HAL9256-这很有帮助!然而,正如你写的,我得到了$sum是5*4。数学计算似乎并没有像我预期的那样在写输出命令中进行。但是,这可以通过将一行代码分成两行来轻松解决:$retval=$one*$two和Write Output$retvalArgh,@RichBayless是的,你是对的。简单地将一些括号括在$one*$two周围,会导致它计算表达式,而不是输出原始字符串输出。我已经编辑了答案以更正它。有趣的是,我从我之前的测试中复制了这个表达式,在这个测试中,我没有写入输出,而是返回$1*$2,不带括号,它成功了;-啊,所有这些测试边缘案例!除非另有规定,否则所有输出都发送到标准输出,因此返回和写入输出是冗余的。我建议使用[cmdletbinding]和Write-Verbose.Nitpicking;,而不是使用Write-Host然后用一个空格作为默认值或变量$OFS的内容将它们连接起来。引用Get Help about_automatic_Variables:$OFS是一个特殊的变量,它存储一个字符串,您想将该字符串用作输出字段分隔符+1挑剔;-然后用一个空格作为默认值或变量$OFS的内容将它们连接起来。引用Get Help about_automatic_Variables:$OFS是一个特殊的变量,它存储一个字符串,您想将该字符串用作输出字段分隔符+1@HAL9256指出了我的另一个错误。。。我忘了在我的数学计算中加括号。。。奇怪的是,return并不关心,但Write-Output却关心。。。我会相应地更新上面这个答案中的示例代码。。。我认为区别在于$sum在提示符处的计算方式与数组对象类似,提示符将遍历数组并在新行上输出ToString,而在引号内包装时,它遍历数组并将ToString添加到现有字符串中,例如替换。由于字符串中没有换行符,因此生成的字符串输出都在同一行上。仅供参考:如果我使用Write Host$sum而不仅仅是$sum,那么我在原始问题的代码中也会得到同样古怪的结果。。。因为只指定变量的隐式功能确实不同于显式写主机命令。@HAL9256指出了我的另一个错误。。。我忘了在我的数学计算中加括号。。。奇怪的是,return并不关心,但Write-Output却关心。。。我会相应地更新上面这个答案中的示例代码。。。我认为区别在于$sum在提示符处的计算方式与数组对象类似,提示符将遍历数组并在新行上输出ToString,而在引号内包装时,它遍历数组并将ToString添加到现有字符串中,例如替换。由于字符串中没有换行符,因此生成的字符串输出都在同一行上。仅供参考:如果我使用Write Host$sum而不仅仅是$sum,那么我在原始问题的代码中也会得到同样古怪的结果。。。因为只指定变量的隐式功能确实不同于显式写主机命令。这是ToString方法的基本行为。这是ToString方法的基本行为。。