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

m4r*_*kus 5 powershell return-value

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

一些背景:

在我们的脚本和模块中,我们总是希望设置$ErrorActionPreference$DebugPreference使用函数可检索的全局默认值.假设我们要设置的$DebugPreference,以"Continue"有在我们的日志的其他信息.

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

$DebugPreference = Get-GlobalDebugPreference
Run Code Online (Sandbox Code Playgroud)

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

问题:

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

我们发现了什么:

无论是否Write-Debug有效,都会以某种方式与"Continue从管理全局默认值的函数返回值的方式相关联 .

例如,当函数看起来像下面的示例时,Write-Debug按预期运行并在PowerShell 3中打印调试消息.但是,在PowerShell 2中,该值设置为"Continue",但Write-Debug不会打印任何其他消息.

function Get-GlobalDebugPreference
{
    return "Continue"
}
$DebugPreference = Get-GlobalDebugPreference
Run Code Online (Sandbox Code Playgroud)

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

function Get-GlobalDebugPreference
{
    "Continue"
}
$DebugPreference = Get-GlobalDebugPreference
Run Code Online (Sandbox Code Playgroud)

有多种方法可以从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
Run Code Online (Sandbox Code Playgroud)

运行它"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
Run Code Online (Sandbox Code Playgroud)

剧本:

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"
Run Code Online (Sandbox Code Playgroud)

Ste*_*NLD 1

早些时候我说过我没有遇到这个问题,但运行代码的方式似乎很重要,不包括 ISE、控制台和以 v2 运行的更高 Powershell 版本,这是我发现的:

没有问题:

  • powershell.exe -文件 c:\debugtest.ps1
  • Powershell.exe -命令 ."c:\debugtest.ps1"

有问题:

  • powershell.exe -命令 c:\debugtest.ps1

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

如果这是问题所在,您可能需要尝试(Get-GlobalDefaultWithReturn).tostring() 将其强制为字符串。

Powershell版本:

Major  Minor  Build  Revision
-----  -----  -----  --------
2      0      -1     -1      
Run Code Online (Sandbox Code Playgroud)

它看起来像一个错误,但我还不知道更多。确实,这有点不可预测,但您可能需要考虑运行代码的方式,例如,如果它是文件,则使用 FILE。