Передача статуса -Verbose командлетам модуля

У меня есть модуль PowerShell, который инкапсулирует ряд часто используемых бизнес-функций. Обычно он не вызывается из консоли; скорее, его функции вызываются автоматическими сценариями развертывания и управления, которые импортируют модуль.

Модуль включает в себя функцию ведения журнала, которая выполняет запись в централизованное место ведения журнала. Я также хотел бы подключиться к функциональности Write-Verbose для записи на консоль.

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

import-module Corporate
Write-Logger 'Foo'

Мое ограничение заключается в том, что - из модуля Corporate PowerShell - мне нужно определить, был ли вызван Script.ps1 с параметром -Verbose. В идеале я хотел бы, чтобы код определения целиком находился внутри самого модуля.

Вот пример:

[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.

4 ответа

Решение

Существует переменная с именем $VerbosePreference, которую вы можете проверить, чтобы увидеть, как должен обрабатываться подробный вывод. Однако здесь возникают проблемы: скрипты загружаются в отдельную область. Если вы читаете Get-Help about_scopes, вот увидишь:

Автор сценария: 
    Область, которая создается во время работы файла сценария. Только
    Команды в сценарии выполняются в области действия сценария. к
    команды в сценарии, область действия сценария является локальной
    объем.

Вместо этого вы можете добавить скрипт в текущую область, используя нотацию точечного источника. Из того же файла справки, под заголовком "Использование точечной нотации с областью действия" указано, что:

Сценарии и функции следуют всем правилам области видимости. Вы создаете их в
определенной области, и они влияют только на эту область, если вы не используете командлет
параметр или модификатор области, чтобы изменить эту область.

Но вы можете добавить скрипт или функцию к текущей области, используя точку 
обозначение источника. Затем, когда скрипт выполняется в текущей области, любой 
доступны функции, псевдонимы и переменные, которые создает скрипт
в текущем объеме.

Я предлагаю прочитать больше об областях в Get-Help about_scopes справочная глава.

Для быстрой проверки того, работает ли это или нет:

[CmdletBinding ()]
ПАРАМЕТР ()

New-Module -Name TempModule -ScriptBlock {
    функция Show-ModuleVerbosePreference
    {
        [CmdletBinding()]
        ПАРАМЕТР ()

        Write-Host "Подробное предпочтение в функции модуля: $VerbosePreference"
    }
} | Из-Null

функция Show-ScriptVerbosePreference
{
    [CmdletBinding()]
    ПАРАМЕТР ()

    Write-Host "Подробное предпочтение в функции скрипта: $VerbosePreference"
}

Шоу-ScriptVerbosePreference
Шоу-ModuleVerbosePreference

И если мы попытаемся вызвать этот файл сценария, используя разные методы, мы получим следующий вывод:

PS C: \>. \ Verbosity.ps1
Подробное предпочтение в функции скрипта: SilentlyContinue
Подробное предпочтение в функции модуля: SilentlyContinue

PS C:\> .\ Verbosity.ps1 -Verbose
VERBOSE: Экспорт функции "Show-ModuleVerbosePreference".
VERBOSE: функция импорта "Show-ModuleVerbosePreference".
Подробное предпочтение в функции скрипта: Продолжить
Подробное предпочтение в функции модуля: SilentlyContinue

PS C:\> . .\ Verbosity.ps1 
Подробное предпочтение в функции скрипта: SilentlyContinue
Подробное предпочтение в функции модуля: SilentlyContinue

PS C:\> . .\verbosity.ps1 -Verbose
VERBOSE: Экспорт функции "Show-ModuleVerbosePreference".
VERBOSE: функция импорта "Show-ModuleVerbosePreference".
Подробное предпочтение в функции скрипта: Продолжить
Подробное предпочтение в функции модуля: Продолжить

Поэтому, используя нотацию с точечным источником, мы добавили область действия скрипта в текущую область, что, кажется, делает настройку VerbosePreference видимой и в методе модуля.

Можно передать большинство общих параметров, используя соответствующие переменные предпочтения и синтаксис, подобный этому, -Parameter:$ParameterPreference, Таким образом, для конкретного случая многословия, синтаксис -Verbose:$VerbosePreference,

Есть несколько исключений:

  • Отладка: значение $DebugPreference автоматически передается, но с указанием -Debug переключить силы $DebugPreference в Inquire,
  • WhatIf: автоматически передается.

Я изменил пример кода OP следующим образом:

[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

В моем.psm1 я помещаю команду, подобную этой:

If ((Get-PSCallStack) [1].Arguments -like '* Verbose = True *') {Write-Host 'Скриптом.ps1, импортирующим этот модуль, является Verbose'};

Вы можете использовать блок скрипта для установки переменной, например, $VerbosePreference в области видимости вашего модуля, или вашей собственной уникальной переменной для вашей собственной логики.

Попробуйте метод ContainsKey:

$PSCmdlet.MyInvocation.BoundParameters.ContainsKey('verbose')
Другие вопросы по тегам