Динамическое создание функций в модуле в PowerShell
Предположим, у меня есть следующий код в модуле (называемый MyModule.psm1, в правильном месте для модуля):
function new-function{
$greeting='hello world'
new-item -path function:\ -name write-greeting -value {write-output $greeting} -Options AllScope
write-greeting
}
После импорта модуля и запуска новой функции я могу успешно вызвать функцию записи приветствия (созданную новой функцией).
Когда я пытаюсь вызвать функцию записи-приветствия вне области вызова новой функции, происходит сбой, потому что функция не существует.
Я пробовал новую функцию точечного поиска, но это не помогает. Я поставил -option Allscope
, но, видимо, это только включает в детей.
Я также попытался явно следовать вызову new-item с помощью команды write-приветствия export-modulemember, которая не выдает ошибку, но также не создает функцию.
Я хочу иметь возможность динамически создавать функцию (т. Е. Через new-item, потому что содержимое и имя функции будет зависеть от ввода) из функции внутри модуля и иметь вновь созданную функцию, доступную для вызова вне модуля.
В частности, я хочу быть в состоянии сделать это:
Import-module MyModule
New-Function
write-greeting
и видеть "привет мир" как вывод
Есть идеи?
2 ответа
Сделать видимую функцию довольно просто: просто измените имя вашей функции в New-Item, чтобы global:
модификатор объема:
new-item -path function:\ -name global:write-greeting -value {write-output $greeting} #-Options AllScope
У вас будет новая проблема с вашим примером, потому что $greeting
будет существовать только в new-function
объем, который не будет существовать, когда вы звоните write-greeting
, Вы определяете модуль с несвязанным блоком сценария, что означает, что он будет искать $greeting
в его области (это не собирается находить это), тогда это будет смотреть в любых родительских областях. Это не увидит тот из new-function
Таким образом, единственный способ получить какой-либо вывод - если модуль или глобальная область видимости содержат $greeting
переменная.
Я не совсем уверен, как будут выглядеть ваши настоящие динамические функции, но самый простой способ обойти новую проблему - создать новое замыкание вокруг вашего блока скриптов следующим образом:
new-item -path function:\ -name global:write-greeting -value {write-output $greeting}.GetNewClosure()
Это создаст новый динамический модуль с копией состояния, доступной в то время. Конечно, это создает новую проблему в том, что функция не исчезнет, если вы вызовете Remove-Module MyModule
, Без дополнительной информации я не уверен, если это проблема для вас или нет...
Вы были близки к тому, чтобы указывать источник, но вам не хватало Export-ModuleMember. Вот полный пример:
function new-function
{
$greeting='hello world'
Invoke-Expression "function write-greeting { write-output '$greeting' }"
write-greeting
}
. new-function
Export-ModuleMember -Function write-greeting
Вы также не нуждались или хотите -Scope AllScope.
Использование квалификатора global: scope работает, но не является идеальным решением. Во-первых, ваша функция может помешать другой функции в глобальной области видимости, чего обычно не должны делать модули. Во-вторых, ваша глобальная функция не будет удалена, если вы удалите модуль. Последнее - ваша глобальная функция не будет определена в области видимости модуля, поэтому, если ей нужен доступ к неэкспортированным функциям или переменным в вашем модуле, вы не сможете (легко) получить к ним доступ.
Благодаря другим решениям я смог придумать небольшого помощника, который позволяет мне добавлять простые файлы сценариев в качестве функций и экспортировать их для модуля за один шаг. Я добавил в свой.psm1 следующую функцию
function AddModuleFileAsFunction {
param (
[string] $Name,
[switch] $Export
)
$content = Get-Content (Join-Path $PSScriptRoot "$Name.ps1") -Raw
# Write-Host $content
$expression = @"
function $Name {
$content
}
"@
Invoke-Expression $expression
if ($Export) {
Export-ModuleMember -Function $Name
}
}
это позволяет мне загружать скрипты как функции:
. AddModuleFileAsFunction "Get-WonderfulThings" -Export
(загружает тело Get-WonderfulThings.ps1 и экспортирует его как функцию:Get-WonderfulThings)