PowerShell函数的不可预知行为返回值

时间:2017-07-24 07:13:41

标签: powershell return-value

当涉及函数返回值时,我们遇到了一些奇怪的PowerShell行为。

某些背景信息:

在我们的脚本和模块中,我们总是希望将$ErrorActionPreference$DebugPreference设置为可通过函数检索的全局默认值。我们假设我们要将$DebugPreference设置为"Continue"以在我们的日志中包含其他消息。

所以,我们正在做这样的事情:

$DebugPreference = Get-GlobalDebugPreference

这是否是一个好方法不是这个问题的一部分。

问题:

在某些具有不同版本PowerShell的计算机上,这并没有按预期工作。当我们调用返回"Continue"的函数时,$DebugPreference确实存储了正确的值,但我们的日志文件中没有任何其他日志消息。

我们发现了什么:

Write-Debug是否有效以某种方式与您返回值的方式相关联 "Continue"来自管理全局默认值的函数。

E.g。当函数类似于以下示例时,Write-Debug按预期运行并在PowerShell 3中打印调试消息。但是,在PowerShell 2中,该值设置为"Continue",但Write-Debug不会打印任何其他消息。

function Get-GlobalDebugPreference
{
    return "Continue"
}
$DebugPreference = Get-GlobalDebugPreference

但是,如果我们只是将值放到shell上并省略return语句,那么它适用于所有版本的PowerShell v2 +。

function Get-GlobalDebugPreference
{
    "Continue"
}
$DebugPreference = Get-GlobalDebugPreference

有多种方法可以从PowerShell中的函数返回值。 对于某些方式,它适用于PS版本2,有些适用于v3。但是,使用Write-Output返回值"Continue"不能达到v5。

在我看来,所有不同的方法应该可以正常工作,并且应该是可以互换的。对于这些基本的事情行为不同是令人担忧的,并使PowerShell有些不可预测。

我编写了一个小脚本,用$DebugPreference设置不同的函数返回方法,这些方法应该都行为相同。如果使用不同版本的PowerShell运行它,则会获得不同数量的Debug输出。请注意,变量$DebugPreference在每个步骤后都具有正确的值和正确的类型,但Write-Debug仅在其中一些步骤之后才起作用。

有人能解释一下这里发生了什么吗?

使用"powershell.exe -version 2 ..."运行它给我这个输出:

Starting to test return values. Current DebugPreference: SilentlyContinue
1 Obtained the value with return: Continue with Type string
2 Obtained the value from shell with return after: Continue with Type string
DEBUG: 2 After Get-GlobalDefaultWithReturnAfterwards
3 Obtained the value from shell with return after: Continue with Type System.Management.Automation.ActionPreference
DEBUG: 3 After Get-GlobalDefaultWithReturnAfterwardsActionPreference
4 Obtained the value without return: Continue with Type string
DEBUG: 4 After Get-GlobalDefaultWithOutReturn
5 Obtained the value with Write-Output: Continue with Type string
6 Obtained the value with Write-Output: Continue with Type System.Management.Automation.ActionPreference
7 Obtained piped value with Write-Output : Continue with Type string
8 Set the value directly: Continue with Type string
DEBUG: 8 After setting the value directly

使用"powershell.exe -version 5 ..."运行它给我这个输出:

Starting to test return values. Current DebugPreference: SilentlyContinue
1 Obtained the value with return: Continue with Type string
DEBUG: 1 After Get-GlobalDefaultWithReturn
2 Obtained the value from shell with return after: Continue with Type string
DEBUG: 2 After Get-GlobalDefaultWithReturnAfterwards
3 Obtained the value from shell with return after: Continue with Type System.Management.Automation.ActionPreference
DEBUG: 3 After Get-GlobalDefaultWithReturnAfterwardsActionPreference
4 Obtained the value without return: Continue with Type string
DEBUG: 4 After Get-GlobalDefaultWithOutReturn
5 Obtained the value with Write-Output: Continue with Type string
6 Obtained the value with Write-Output: Continue with Type System.Management.Automation.ActionPreference
DEBUG: 6 After Get-GlobalDefaultWriteOutputActionPreference
7 Obtained piped value with Write-Output : Continue with Type string
8 Set the value directly: Continue with Type string
DEBUG: 8 After setting the value directly

剧本:

function Get-GlobalDefaultWithReturn
{
    return "Continue"
}

function Get-GlobalDefaultWithReturnAfterwards
{
    "Continue"
    return
}

function Get-GlobalDefaultWithReturnAfterwardsActionPreference
{
    ([System.Management.Automation.ActionPreference]::Continue)
    return
}

function Get-GlobalDefaultWithOutReturn
{
    "Continue"
}

function Get-GlobalDefaultWriteOutput
{
    Write-Output "Continue"
}

function Get-GlobalDefaultWriteOutputActionPreference
{
    Write-Output ([System.Management.Automation.ActionPreference]::Continue)
}


$DebugPreference = "SilentlyContinue"
Write-Host "Starting to test return values. Current DebugPreference: $DebugPreference"

$DebugPreference = Get-GlobalDefaultWithReturn
Write-Host "1 Obtained the value with return: $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "1 After Get-GlobalDefaultWithReturn"

$DebugPreference = "SilentlyContinue"
$DebugPreference = Get-GlobalDefaultWithReturnAfterwards
Write-Host "2 Obtained the value from shell with return after: $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "2 After Get-GlobalDefaultWithReturnAfterwards"

$DebugPreference = "SilentlyContinue"
$DebugPreference = Get-GlobalDefaultWithReturnAfterwardsActionPreference
Write-Host "3 Obtained the value from shell with return after: $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "3 After Get-GlobalDefaultWithReturnAfterwardsActionPreference"

$DebugPreference = "SilentlyContinue"
$DebugPreference = Get-GlobalDefaultWithOutReturn
Write-Host "4 Obtained the value without return: $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "4 After Get-GlobalDefaultWithOutReturn"

$DebugPreference = "SilentlyContinue"
$DebugPreference = Get-GlobalDefaultWriteOutput
Write-Host "5 Obtained the value with Write-Output: $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "5 After Get-GlobalDefaultWriteOutput"

$DebugPreference = "SilentlyContinue"
$DebugPreference = Get-GlobalDefaultWriteOutputActionPreference
Write-Host "6 Obtained the value with Write-Output: $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "6 After Get-GlobalDefaultWriteOutputActionPreference"

$DebugPreference = "SilentlyContinue"
Get-GlobalDefaultWriteOutput | % { $DebugPreference = $_ }
Write-Host "7 Obtained piped value with Write-Output : $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "7 After Get-GlobalDefaultWriteOutput with pipe"

$DebugPreference = "SilentlyContinue"
$DebugPreference = "Continue"
Write-Host "8 Set the value directly: $DebugPreference with Type $($DebugPreference.GetType())"
Write-Debug "8 After setting the value directly"

1 个答案:

答案 0 :(得分:1)

之前我曾说过我没有这个问题,但似乎你运行代码的方式很重要,不包括运行为v2的ISE,Console和Higher Powershell版本,这就是我发现的:

没有问题:

  • powershell.exe -file c:\ debugtest.ps1
  • Powershell.exe -command。" c:\ debugtest.ps1"

有问题:

  • powershell.exe -command c:\ debugtest.ps1

我建议的解决方法在这种情况下有效:

  

你可能想尝试(Get-GlobalDefaultWithReturn).tostring()来强制   如果那是问题就把它变成一个字符串。

Powershell版本:

Major  Minor  Build  Revision
-----  -----  -----  --------
2      0      -1     -1      

它看起来像一个bug,但我还不知道更多关于它的信息。 确实有点不可预测,但您可能想要考虑运行代码的方式,例如:如果它是文件,请使用FILE。