Как управлять областью действия скриптового блока PowerShell

У меня есть функция, написанная в PowerShell:

function replace([string] $name,[scriptblock] $action) {
    Write-Host "Replacing $name"
    $_ = $name
    $action.Invoke()
}

и будет использоваться как:

$name = "tick"
replace $agentPath\conf\buildAgent.dist.properties {
    (cat templates\buildAgent.dist.properties.tpl) `
        -replace '@@serverurl@@', 'http:/localhost:8080/teamcity' `
        -replace '@@name@@', $name `
        > $_
}

Однако я обнаружил, что в блоке сценария переменная $name перезаписывается параметром $name из функции replace.

Есть ли способ выполнить блок сценария, чтобы в область действия блока сценария добавлялась только переменная $_, но больше ничего?


person bradgonesurfing    schedule 03.05.2017    source источник


Ответы (2)


Вы можете использовать префикс $global: для переменной $name в своем блоке сценария:

$name = "tick"
replace $agentPath\conf\buildAgent.dist.properties {
    (cat templates\buildAgent.dist.properties.tpl) `
        -replace '@@serverurl@@', 'http:/localhost:8080/teamcity' `
        -replace '@@name@@', $global:name `
        > $_
}
person Martin Brandl    schedule 03.05.2017
comment
Я мог бы, но это просит меня или другого разработчика забыть однажды, и проблема случайно появится в будущем. Я ожидаю, что будет какое-то решение, работающее с другого конца, когда вызывающая сторона блока скрипта вежлива и не вводит все свои локальные переменные в область видимости. - person bradgonesurfing; 03.05.2017
comment
Я нашел способ сделать это. Это точно некрасиво :) Смотрите мой ответ. - person bradgonesurfing; 03.05.2017
comment
Выглядит некрасиво, но я согласен, что другим разработчикам лучше использовать эту функцию. - person Martin Brandl; 03.05.2017

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

gv '_' получает переменную powershell $_ и передает ее контексту через InvokeWithContext.

Теперь я знаю больше, чем когда-либо хотел :|

New-Module {
    function replace([string] $name,[scriptblock] $action) {
        Write-Host "Replacing $name"
        $_ = $name
        $action.InvokeWithContext(@{}, (gv '_'))
    }
}

и как раньше

$name = "tick"
replace $agentPath\conf\buildAgent.dist.properties {
    (cat templates\buildAgent.dist.properties.tpl) `
        -replace '@@serverurl@@', 'http:/localhost:8080/teamcity' `
        -replace '@@name@@', $name `
        > $_
}
person bradgonesurfing    schedule 03.05.2017