传递“-Verbose”状态到模块的命令

17

我有一个PowerShell模块,包含多个常用的业务函数。这个模块通常不会从控制台中调用,而是被导入到自动化部署和管理脚本中以调用其函数。

该模块还包含一个记录日志的功能,可以将日志信息写入到一个集中的日志位置。我还想利用Write-Verbose功能将日志信息写入到控制台。

#'Start Script.ps1
#'----------------

Import-Module Corporate
Write-Logger 'Foo'

我的限制是,在企业 PowerShell 模块内部,我需要确定是否使用了 -Verbose 参数调用了 Script.ps1。理想情况下,我希望确定代码完全在模块内部。

以下是一个示例:

[CmdletBinding()]
Param ()

New-Module -Name TempModule -ScriptBlock {
    function Test-ModuleVerbose() {
        [CmdletBinding()]
        Param ()

        PROCESS {
            $vb = ($PSCmdlet.MyInvocation.BoundParameters['Verbose'] -eq $true)
            Write-Host ("1: Module verbose preference: " + ($PSCmdlet.MyInvocation.BoundParameters['Verbose'] -eq $true))
            Write-Host ("2: Module verbose preference: " + $Script:VerbosePreference)
            Write-Host ("3: Module verbose preference: " + $VerbosePreference)
        }
    }
} | Out-Null

function Test-Verbose() {
    [CmdletBinding()]
    Param ()

    PROCESS {
        Write-Host ("Verbose preference: $VerbosePreference")
        Test-ModuleVerbose
    }
}

Test-Verbose

将上述内容保存为 test.ps1。在控制台中调用时:
PS C:\temp> .\test.ps1
Verbose preference: SilentlyContinue
1: Module verbose preference: False
2: Module verbose preference:
3: Module verbose preference: SilentlyContinue

PS C:\temp> .\test.ps1 -Verbose
VERBOSE: Exporting function 'Test-ModuleVerbose'.
VERBOSE: Importing function 'Test-ModuleVerbose'.
Verbose preference: Continue
1: Module verbose preference: False
2: Module verbose preference:
3: Module verbose preference: SilentlyContinue

正如您所看到的,$VerbosePreference变量在模块内部不可用。是否有一种方式可以从模块内部获取调用脚本是否已使用-Verbose标志?


你可以为脚本声明 switch 参数,然后根据它的值设置一些全局变量。同时,你也可以设置/取消环境变量,并在 cmdlet 中检查它的值。 - user2060111
4个回答

17
可以使用匹配偏好变量和类似以下语法的方式,通过传递大多数常见参数。例如,对于详细信息的特定情况,语法为-Verbose:$VerbosePreference。但也有几个例外情况:
- Debug:自动传递$DebugPreference的值,但指定-Debug开关会强制将$DebugPreference设置为Inquire。 - WhatIf:自动传递。
我已修改原始代码示例,如下所示:
[CmdletBinding(SupportsShouldProcess=$true)]
param(
    [Switch]$FullPassThru
)

New-Module -Name TempModule -ScriptBlock {
        function Test-ModuleVerbose
        {
            [CmdletBinding(SupportsShouldProcess=$true)]
            param ()

            Write-Host "1: Module: verbose parameter is bound : $($PSCmdlet.MyInvocation.BoundParameters['Verbose'])"
            Write-Host "2: Module: verbose preference         : $VerbosePreference"

            # Write-Verbose will just work without any change
            Write-Verbose "Verbose"

            # Other commands need the $VerbosePreference passed in
            Set-Item -Path Env:\DEMONSTRATE_PASS_THRU `
                     -Value 'You can safely delete this variable' `
                     -Verbose:$VerbosePreference
        }

        function Test-ModulePreferencePassThru
        {
            [CmdletBinding(SupportsShouldProcess=$true)]
            param()

            Write-Debug   "DebugPreference: $DebugPreference"
            Write-Warning "WarningPreference: $WarningPreference"
            Write-Error   "ErrorActionPreference: $ErrorActionPreference"

            Set-Item -Path Env:\DEMONSTRATE_PASS_THRU `
                     -Value 'You can safely delete this variable' `
                     -Verbose:$VerbosePreference `
                     -WarningAction:$WarningPreference `
                     -ErrorAction:$ErrorActionPreference
        }
    } | Out-Null

function Test-Verbose
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    param()

    Write-Host ("Verbose preference: $VerbosePreference")
    Test-ModuleVerbose -Verbose:$VerbosePreference
}

function Test-PreferencePassThru
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    param()

    Test-ModulePreferencePassThru -Verbose:$VerbosePreference
}

try
{
    if ($FullPassThru -eq $false)
    {
        # just demonstrate -verbose pass-through
        Test-Verbose
    }
    else
    {
        # most of the preferences can be explicitly passed-through, however:
        #
        #  -Debug  : $DebugPreference is automatically passed-through
        #            and -Debug forces $DebugPreference to 'Inquire'
        #  -WhatIf : automatically passed-through
        Test-ModulePreferencePassThru -Verbose:$VerbosePreference `
                                        -WarningAction:$WarningPreference `
                                        -ErrorAction:$ErrorActionPreference | Out-Null
    }
}
finally
{
    # cleanup
    Remove-Item -Path Env:\DEMONSTRATE_PASS_THRU -Force | Out-Null
}

将上述内容保存为test.ps1。从控制台调用时:

PS C:\temp> .\test.ps1
Verbose preference: SilentlyContinue
1: Module: verbose parameter is bound : False
2: Module: verbose preference         : SilentlyContinue

PS C:\temp> .\test.ps1 -Verbose
VERBOSE: Exporting function 'Test-ModuleVerbose'.
VERBOSE: Exporting function 'Test-ModulePreferencePassThru'.
VERBOSE: Importing function 'Test-ModulePreferencePassThru'.
VERBOSE: Importing function 'Test-ModuleVerbose'.
Verbose preference: Continue
1: Module: verbose parameter is bound : True
2: Module: verbose preference         : Continue
VERBOSE: Verbose
VERBOSE: Performing the operation "Set Item" on target "Item: DEMONSTRATE_PASS_THRU Value: You can safely delete this variable".

另外,对于$DebugPreference$WarningPreference$ErrorActionPreference,传递也是有效的:

PS C:\temp> $VerbosePreference  = 'Continue'
PS C:\temp> $DebugPreference = 'Continue'
PS C:\temp> $WarningPreference = 'Continue'
PS C:\temp> $ErrorActionPreference = 'Continue'
PS C:\temp> .\test.ps1 -FullPassThru
VERBOSE: Exporting function 'Test-ModuleVerbose'.
VERBOSE: Exporting function 'Test-ModulePreferencePassThru'.
VERBOSE: Importing function 'Test-ModulePreferencePassThru'.
VERBOSE: Importing function 'Test-ModuleVerbose'.
DEBUG: DebugPreference: Continue
WARNING: WarningPreference: Continue
Test-ModulePreferencePassThru : ErrorActionPreference: Continue
At C:\OAASMain\Online\ContainerService\Tools\docker\test.ps1:72 char:9
+         Test-ModulePreferencePassThru -Verbose:$VerbosePreference `
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-ModulePreferencePassThru

VERBOSE: Performing the operation "Set Item" on target "Item: DEMONSTRATE_PASS_THRU Value: You can safely delete this variable".

-WhatIf参数会自动传递:

PS C:\temp> .\test.ps1 -FullPassThru -WhatIf
What if: Performing the operation "Remove Item" on target "Item: DEMONSTRATE_PASS_THRU".

这也处理-WarningAction-ErrorAction

PS C:\temp> .\test.ps1 -FullPassThru -WarningAction Ignore -ErrorAction Stop
Test-ModulePreferencePassThru : ErrorActionPreference : Stop
At C:\OAASMain\Online\ContainerService\Tools\docker\test.ps1:72 char:9
+         Test-ModulePreferencePassThru -Verbose:$VerbosePreference `
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-ModulePreferencePassThru

5
-Verbose是一个开关选项,而不是一个字符串或集合参数。因此,将-Verbose:$VerbosePreference进行设置会导致错误:无法转换'System.String'到类型'System.Management.Automation.SwitchParameter',参数'Verbose'所需的. 在PowerShell 7中至少如此。正确的语法来传递Verbose是-Verbose:($VerbosePreference -eq 'Continue') - Björn Jarisch

11

您可以检查名为$VerbosePreference的变量,以查看如何处理冗长输出。但是,加载到单独作用域中的脚本会引起问题。如果您阅读Get-Help about_scopes,您将会看到:

脚本:
在运行脚本文件时创建的作用域。 仅脚本中的命令在脚本作用域中运行。 对于脚本中的命令,脚本作用域是本地作用域。

您可以使用点号源代码将脚本添加到当前作用域中。从相同的帮助文件,在标题在作用域中使用点号源代码下述:

脚本和函数遵循所有作用域规则。您在特定作用域中创建它们,除非使用cmdlet参数或作用域修改器更改该作用域,否则它们仅影响该作用域。

但是,您可以使用点号源代码将脚本或函数添加到当前作用域中。然后,当脚本在当前作用域中运行时,脚本创建的任何函数、别名和变量都可在当前作用域中使用。

我建议在Get-Help about_scopes帮助章节中进一步了解作用域。

如果要快速测试这是否有效:

[CmdletBinding()]
PARAM()

New-Module -Name TempModule -ScriptBlock {
    function Show-ModuleVerbosePreference
    {
        [CmdletBinding()]
        PARAM()

        Write-Host "Verbose preference in module function: $VerbosePreference"
    }
} | Out-Null

function Show-ScriptVerbosePreference
{
    [CmdletBinding()]
    PARAM()

    Write-Host "Verbose preference in script function: $VerbosePreference"
}

Show-ScriptVerbosePreference
Show-ModuleVerbosePreference</pre>

如果我们尝试使用不同的方法调用此脚本文件,我们将得到以下输出:

PS C:\> .\verbosity.ps1
Verbose preference in script function: SilentlyContinue
Verbose preference in module function: SilentlyContinue

PS C:\> .\verbosity.ps1 -Verbose
VERBOSE: Exporting function 'Show-ModuleVerbosePreference'.
VERBOSE: Importing function 'Show-ModuleVerbosePreference'.
Verbose preference in script function: Continue
Verbose preference in module function: SilentlyContinue

PS C:\> . .\verbosity.ps1
Verbose preference in script function: SilentlyContinue
Verbose preference in module function: SilentlyContinue

PS C:\> . .\verbosity.ps1 -Verbose
VERBOSE: Exporting function 'Show-ModuleVerbosePreference'.
VERBOSE: Importing function 'Show-ModuleVerbosePreference'.
Verbose preference in script function: Continue
Verbose preference in module function: Continue
所以通过使用点号源符号,我们已将脚本范围添加到当前范围中,这似乎使得VerbosePreference设置在模块方法中也可见。

感谢@robert.westerlund。我觉得我没有很好地解释清楚自己。我知道如何在脚本中检查Verbose标志是否设置。我需要的是能够从PowerShell模块内部检查脚本是否使用Verbose标志调用。我已经更新了我的问题以更好地解释它。 - Dan
抱歉,我误解了你的意思。我认为你需要研究的是PowerShell作用域。我会更新我的答案。 - Robert Westerlund
1
感谢@robert.westerlund。我认为点源脚本可能是我能得到的最接近的东西。 虽然看起来很奇怪 - 因为模块被设计为可重用的代码片段,可以从多个地方调用,你会认为可以获取脚本范围变量。 感谢您的帮助。 - Dan
我建议阅读作用域相关的内容(Get-Help about_scopes),这样你就可以了解它是如何工作的。:) 我之前也遇到过同样的问题,也许你会找到比上面提供的更好的解决方案(如果有的话,请编辑我的回答)。 - Robert Westerlund
@Dan,我相信我的答案解决了你如何将“-Verbose”和其他常见参数全部传递的问题。 - Mike
尝试运行 "Get-Help about_scopes" 时,我遇到了一个错误。但这是 PowerShell 的问题,以管理员身份运行 Update-Help 可以解决它。(Windows 10) - Peter Mortensen

2
在我的.psm1文件中,我放置了一个类似于以下命令的命令:
If ((Get-PSCallStack)[1].Arguments -like '\*Verbose=True\*') {
    Write-Host 'The .ps1 script importing this module is Verbose'
};

您可以使用脚本块来设置一个变量,例如在您的模块范围内设置$VerbosePreference,或者为您自己的逻辑设置一个独特的变量。

-1

尝试使用ContainsKey方法:

$PSCmdlet.MyInvocation.BoundParameters.ContainsKey('verbose')

1
当在模块内执行时,BoundParameters集合始终为空。 - Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接