Примечание: Я использую 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, но кто-то может объяснить это поведение мне?
Я исследовал эту проблему, которая возникла в проекте, над которым я работаю, и обнаружил три вещи:
scriptBlock
, физически расположен где-нибудь в файле .psm1, мы видим поведение. scriptBlock
, находится в отдельном файле сценария (.ps1), если scriptBlock
был передан в из модуль. scriptBlock
, находится где-нибудь в файле сценария (.ps1), пока scriptBlock
] не был передан из модуля. scriptBlock
не обязательно будет выполняться в глобальной области. Скорее, он всегда выполняется в той области, из которой была вызвана функция модуля. scriptBlock
: "." оператор, оператор «&» и метод invoke () объекта scriptBlock
. В последних двух случаях scriptBlock
выполняется с неправильной родительской областью.Это можно исследовать, пытаясь вызвать, например, {set-variable -name "message" -scope 1 -value "From scriptBlock"}
Я надеюсь, что это проливает некоторый свет на проблему, хотя я не знаю не зашел достаточно далеко, чтобы предложить обходной путь.
У кого-нибудь еще установлен PowerShell 1? Если да, было бы полезно, если бы вы могли проверить, показывает ли он такое же поведение.
Вот файлы для моих тестовых случаев. Чтобы запустить их, создайте все четыре файла в одном каталоге, а затем выполните "./all_tests.ps1" в командной строке PowerShell ISE
param($script_block)
set-alias "wh" write-host
$message = "Script message"
wh " Script message before: '$message'"
. $script_block
wh " Script message after: '$message'"
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'"
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"
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"
Похоже, что $ 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, чтобы проверить, могу ли я это подтвердить.