Странное поведение с объемом переменной блока сценария Powershell и модулями, какими-либо предложениями?

Примечание: Я использую PowerShell 2.0 на Windows Vista.

Я пытаюсь добавить поддержку определения, что аргументы сборки говорили, но я столкнулся с некоторой странной переменной PowerShell обзор поведения, имеющего дело конкретно с вызыванием функций, которые были экспортированы с помощью Экспорта-ModuleMember (который является, как говорил, представляет, это - основной метод). Следующее является простым модулем PowerShell для иллюстрирования (названный repoCase.psm1):

function Test {
    param(
        [Parameter(Position=0,Mandatory=0)]
        [scriptblock]$properties = {}
    )

    $defaults = {$message = "Hello, world!"}

    Write-Host "Before running defaults, message is: $message"

    . $defaults

    #At this point, $message is correctly set to "Hellow, world!"
    Write-Host "Aftering running defaults, message is: $message"

    . $properties

    #At this point, I would expect $message to be set to whatever is passed in,
    #which in this case is "Hello from poperties!", but it isn't.  
    Write-Host "Aftering running properties, message is: $message"
}

Export-ModuleMember -Function "Test"

Для тестирования модуля выполните следующую последовательность команд (быть уверенным, что Вы находитесь в том же каталоге как repoCase.psm1):

Import-Module .\repoCase.psm1

#Note that $message should be null
Write-Host "Before execution - In global scope, message is: $message"

Test -properties { "Executing properties, message is $message"; $message = "Hello from properties!"; }

#Now $message is set to the value from the script block.  The script block affected only the global scope.
Write-Host "After execution - In global scope, message is: $message"

Remove-Module repoCase

Поведение, которое я ожидал, было для блока сценария, который я передал Тесту для влияния на локальный объем Теста. Это - 'dotsourced' в, таким образом, любые изменения, которые это вносит, должны быть в рамках вызывающей стороны. Однако это не то, что происходит, это, кажется, влияет на объем того, где это было объявлено. Вот вывод:

Before execution - In global scope, message is:
Before running defaults, message is:
Aftering running defaults, message is: Hello, world!
Executing properties, message is
Aftering running properties, message is: Hello, world!
After execution - In global scope, message is: Hello from properties!

Интересно, если я не экспортирую Тест как модуль и вместо этого просто объявляю функцию и вызываю его, все работает точно так же, как я ожидал бы это к. Блок сценария влияет только на объем Теста и не изменяет глобальную область видимости.

Я не гуру PowerShell, но кто-то может объяснить это поведение мне?

16
задан Matt Honeycutt 3 February 2010 в 16:15
поделиться

2 ответа

Я исследовал эту проблему, которая возникла в проекте, над которым я работаю, и обнаружил три вещи:

  1. Проблема специфична для модулей.
    • Если код, вызывающий scriptBlock , физически расположен где-нибудь в файле .psm1, мы видим поведение.
    • Мы также видим поведение, если код, вызывающий scriptBlock , находится в отдельном файле сценария (.ps1), если scriptBlock был передан в из модуль.
    • Мы не видим поведение, если код, который вызвал scriptBlock , находится где-нибудь в файле сценария (.ps1), пока scriptBlock ] не был передан из модуля.
  2. scriptBlock не обязательно будет выполняться в глобальной области. Скорее, он всегда выполняется в той области, из которой была вызвана функция модуля.
  3. Проблема не ограничивается "." оператор (источник). Я протестировал три различных способа вызова scriptBlock : "." оператор, оператор «&» и метод invoke () объекта scriptBlock . В последних двух случаях scriptBlock выполняется с неправильной родительской областью.Это можно исследовать, пытаясь вызвать, например, {set-variable -name "message" -scope 1 -value "From scriptBlock"}

Я надеюсь, что это проливает некоторый свет на проблему, хотя я не знаю не зашел достаточно далеко, чтобы предложить обходной путь.

У кого-нибудь еще установлен PowerShell 1? Если да, было бы полезно, если бы вы могли проверить, показывает ли он такое же поведение.

Вот файлы для моих тестовых случаев. Чтобы запустить их, создайте все четыре файла в одном каталоге, а затем выполните "./all_tests.ps1" в командной строке PowerShell ISE

script_toplevel.ps1

param($script_block)

set-alias "wh" write-host

$message = "Script message"
wh "  Script message before:      '$message'"
. $script_block
wh "  Script message after:       '$message'"

script_infunction.ps1

param($script_block)
set-alias "wh" write-host

function f {
    param($script_block)
    $message = "Function message"
    wh "  Function message before:    '$message'"
    . $script_block
    wh "  Function message after:     '$message'"
}

$message = "Script message"
wh "  Script message before:      '$message'"
f -script_block $script_block
wh "  Script message after:       '$message'"

module.psm1

set-alias "wh" write-host

function simple_test_fun {
    param($script_block)

    $message = "ModFunction message"
    wh "  ModFunction message before: '$message'"
    . $script_block
    wh "  ModFunction message after:  '$message'"
}

function ampersand_test_fun {
    param($script_block)

    $message = "ModFunction message"
    wh "  ModFunction message before: '$message'"
    & $script_block
    wh "  ModFunction message after:  '$message'"
}

function method_test_fun {
    param($script_block)

    $message = "ModFunction message"
    wh "  ModFunction message before: '$message'"
    $script_block.invoke()
    wh "  ModFunction message after:  '$message'"
}

function test_mod_to_script_toplevel {
    param($script_block)

    $message = "ModFunction message"
    wh "  ModFunction message before: '$message'"
    & .\script_toplevel.ps1 -script_block $script_block
    wh "  ModFunction message after:  '$message'"
}

function test_mod_to_script_function {
    param($script_block)

    $message = "ModFunction message"
    wh "  ModFunction message before: '$message'"
    & .\script_infunction.ps1 -script_block $script_block
    wh "  ModFunction message after:  '$message'"
}

export-modulemember -function "simple_test_fun", "test_mod_to_script_toplevel", "test_mod_to_script_function", "ampersand_test_fun", "method_test_fun"

all_tests.ps1

remove-module module
import-module .\module.psm1

set-alias "wh" write-host

wh "Test 1:"
wh "  No problem with . at script top level"
wh "    ScriptBlock created at 'TopScript' scope"
wh "    TopScript -amp-calls-> Script -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: Script message after:       'Script block message'"
wh "  Problem behavior:  TopScript message after:    'Script block message'"
wh
wh "Results:"

$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
& .\script_toplevel.ps1 -script_block {$message = "Script block message"}
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 1 showed expected behavior"
wh
wh
wh "Test 2:"
wh "  No problem with . inside function in script"
wh "    ScriptBlock created at 'TopScript' scope"
wh "    TopScript -amp-calls-> Script -calls-> Function -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: Function message after:     'Script block message'"
wh "  Problem behavior:  TopScript message after:    'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
& .\script_infunction.ps1 -script_block {$message = "Script block message"}
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 2 showed expected behavior"
wh
wh
wh "Test 3:"
wh "  Problem with with . with function in module"
wh "    ScriptBlock created at 'TopScript' scope"
wh "    TopScript -calls-> ModFunction -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: ModFunction message after:  'Script block message'"
wh "  Problem behavior:  TopScript message after:    'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
simple_test_fun -script_block {$message = "Script block message"}
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 3 showed problem behavior"
wh
wh
wh "Test 4:"
wh "  Confirm that problem scope is always scope where ScriptBlock is created"
wh "    ScriptBlock created at 'f1' scope"
wh "    TopScript -calls-> f1 -calls-> f2 -amp-calls-> ModFunction -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: ModFunction message after:  'Script block message'"
wh "  Problem behavior:  f1 message after:           'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
function f1 {
    $message = "f1 message"
    wh "  f1 message before:          '$message'"
    f2 -script_block {$message = "Script block message"}
    wh "  f1 message after:           '$message'"
}
function f2 {
    param($script_block)

    $message = "f2 message"
    wh "  f2 message before:          '$message'"
    simple_test_fun -script_block $script_block
    wh "  f2 message after:           '$message'"
}

f1
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 4 showed problem behavior"
wh
wh
wh "Test 4:"
wh "  Confirm that problem scope is always scope where ScriptBlock is created"
wh "    ScriptBlock created at 'f1' scope"
wh "    TopScript -calls-> f1 -calls-> f2 -amp-calls-> ModFunction -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: ModFunction message after:  'Script block message'"
wh "  Problem behavior:  f1 message after:           'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
function f1 {
    $message = "f1 message"
    wh "  f1 message before:          '$message'"
    f2 -script_block {$message = "Script block message"}
    wh "  f1 message after:           '$message'"
}
function f2 {
    param($script_block)

    $message = "f2 message"
    wh "  f2 message before:          '$message'"
    simple_test_fun -script_block $script_block
    wh "  f2 message after:           '$message'"
}

f1
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 4 showed problem behavior"
wh
wh
wh "Test 5:"
wh "  Problem with with . when module function invokes script (toplevel)"
wh "    ScriptBlock created at 'TopScript' scope"
wh "    TopScript -calls-> ModFunction -amp-calls-> Script -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: ModFunction message after:  'Script block message'"
wh "  Problem behavior:  TopScript message after:    'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
test_mod_to_script_toplevel -script_block {$message = "Script block message"}
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 5 showed problem behavior"
wh
wh
wh "Test 6:"
wh "  Problem with with . when module function invokes script (function)"
wh "    ScriptBlock created at 'TopScript' scope"
wh "    TopScript -calls-> ModFunction -amp-calls-> Script -calls-> function -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: ModFunction message after:  'Script block message'"
wh "  Problem behavior:  TopScript message after:    'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
test_mod_to_script_function -script_block {$message = "Script block message"}
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 6 showed problem behavior"
wh
wh
wh "Test 7:"
wh "  Problem with with & with function in module"
wh "    ScriptBlock created at 'TopScript' scope"
wh "    TopScript -calls-> ModFunction -amp-calls-> Script -calls-> function -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: ModFunction message after:  'Script block message'"
wh "  Problem behavior:  TopScript message after:    'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
ampersand_test_fun -script_block {set-variable -scope 1 -name "message" -value "Script block message"}
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 7 showed problem behavior"
wh
wh
wh "Test 8:"
wh "  Problem with with invoke() method with function in module"
wh "    ScriptBlock created at 'TopScript' scope"
wh "    TopScript -calls-> ModFunction -amp-calls-> Script -calls-> function -dot-calls-> ScriptBlock:"
wh
wh "  Expected behavior: ModFunction message after:  'Script block message'"
wh "  Problem behavior:  TopScript message after:    'Script block message'"
wh
wh "Results:"
$global:message = "Global message"
$message = "Top script message"
wh "  Global message before:      '$global:message'"
wh "  TopScript message before:   '$message'"
method_test_fun -script_block {set-variable -scope 1 -name "message" -value "Script block message"}
wh "  TopScript message after:    '$message'"
wh "  Global message after:       '$global:message'"

wh
wh "Test 8 showed problem behavior"
7
ответ дан 30 November 2019 в 22:17
поделиться

Похоже, что $ message в переданном блоке сценария привязано к глобальной области, например:

function Test { 
    param( 
        [Parameter(Position=0,Mandatory=0)] 
        [scriptblock]$properties = {} 
    ) 

    $defaults = {$message = "Hello, world!"} 

    Write-Host "Before running defaults, message is: $message" 

    . $defaults 

    #At this point, $message is correctly set to "Hellow, world!" 
    Write-Host "Aftering running defaults, message is: $message" 

    . $properties 

    #At this point, I would expect $message to be set to whatever is passed in, 
    #which in this case is "Hello from poperties!", but it isn't.   
    Write-Host "Aftering running properties, message is: $message" 

    # This works. Hmmm
    Write-Host "Aftering running properties, message is: $global:message" 
} 

Export-ModuleMember -Function "Test" 

Вывод:

Before running defaults, message is: 
Aftering running defaults, message is: Hello, world!
Executing properties, message is 
Aftering running properties, message is: Hello, world!
Aftering running properties, message is: Hello from properties!

Похоже, это ошибка. Я воспользуюсь списком PowerShell MVP, чтобы проверить, могу ли я это подтвердить.

4
ответ дан 30 November 2019 в 22:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: