Наконечник шляпы PetSerAl за всю его помощь.
Вот простое решение , но обратите внимание, что оно запускает блок сценария непосредственно в области действия вызывающего , то есть он фактически «точечные источники», что позволяет модифицировать переменные вызывающего объекта.
В отличие от этого, использование Invoke-Command
запускает блок скрипта в child области видимости вызывающей стороны - если это действительно намерение, см. Вариант решения ниже.
"Dot-sourcing" блок сценария - это то, что делают стандартные командлеты, такие как Where-Object
и ForEach-Object
.
# Define the function in an (in-memory) module.
# An in-memory module is automatically imported.
$null = New-Module {
function PipeScript {
param(
[Parameter(ValueFromPipeline)]
[Object] $InputObject
,
[scriptblock] $ScriptBlock
)
process {
# Use ForEach-Object to create the automatic $_ variable
# in the script block's origin scope.
$value = ForEach-Object -Process $ScriptBlock -InputObject $InputObject
# Output the value
"Script: $value"
}
}
}
# Test the function:
$var = 42; @{ Name = 'Test' } | PipeScript -ScriptBlock { $_.Name; ++$var }
$var # -> 43 - the script block ran in the caller's scope.
Вышеприведенные строки вывода Script: Test
и 43
впоследствии подтверждаютчто входной объект был замечен как $_
, и что точечный источник работал ($var
был успешно увеличен в объеме вызывающего абонента).
Вот вариант , черезPowerShell SDK, который запускает блок сценария в дочерней области действия вызывающей стороны .
Это может быть полезно, если вы не хотите выполнять сценарийблок для случайного изменения переменных вызывающего.
Это то же самое поведение, которое вы получаете с функциями сценария-блока привязки на уровне двигателя и с вычисляемым свойством - хотя неясно, было ли это поведение выбрано преднамеренно.
$null = New-Module {
function PipeScript {
param(
[Parameter(ValueFromPipeline)]
[Object] $InputObject
,
[scriptblock] $ScriptBlock
)
process {
# Use ScriptBlock.InvokeContext() to inject a $_ variable
# into the child scope that the script block runs in:
# Creating a custom version of what is normally an *automatic* variable
# seems hacky, but the docs do state:
# "The list of variables may include the special variables
# $input, $_ and $this." - see https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.scriptblock.invokewithcontext
$value = $ScriptBlock.InvokeWithContext(
$null, # extra functions to define (none here)
[psvariable]::new('_', $InputObject) # actual parameter type is List<PSVariable>
)
# Output the value
"Script: $value"
}
}
}
# Test the function:
$var = 42
@{ Name = 'Test' } | PipeScript -ScriptBlock { $_.Name; ++$var }
$var # -> 42 - unaltered, because the script block ran in a child scope.
Вышеприведенная строка выводит Script: Test
, затем следует 42
, доказывая, что блок скрипта видел входной объект как $_
и эту переменную $var
- хотя видно в блоке сценария не был изменен из-за выполнения в дочерней области.
Метод ScriptBlock.InvokeWithContext()
задокументирован здесь .
Что касается , почему ваша попытка не сработала :
Как правило, блоки скриптов привязаны к области действия и области действия, в которой они находятся создал (кроме случаев, когда они явно созданы как несвязанные блоки сценариев с [scriptblock]::Create('...')
).
Область за пределами модуля является частьюдомен области по умолчанию.Каждый модуль имеет свой собственный домен области, и, за исключением глобальной области, которую видят все области во всех доменах области, области в разных областях области не видят друг друга.
Ваш блок скриптасоздается в домене области действия по умолчанию, и когда определяемая модулем функция вызывает его, $_
ищется в области происхождения , т. е. в (немодульной) вызывающей сторонеОбласть действия , где она не определена, поскольку автоматическая переменная $_
создается PowerShell по требованию в области local , которая находится в области области действия включающего модуля.
При использовании .InvokeWithContext()
блок скрипта запускается в child scope области вызывающего абонента (как было бы в случае с .Invoke()
и Invoke-Command
по умолчанию) вкоторый приведенный выше код внедряет пользовательскую переменную $_
, чтобы блок сценария мог ссылаться на нее.
Обеспечение улучшенная поддержка SDK для этих сценариев обсуждается в этот выпуск GitHub .