bradgonesurfing
bradgonesurfing

Reputation: 32192

How to control the scope of a PowerShell scriptblock

I have a function written in PowerShell:

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

and would be used as:

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

However I've discovered that within the scriptblock the variable $name is overwritten by the $name param from within the replace function.

Is there a way to execute the script block so that only the variable $_ is added to the scope of the scriptblock but nothing else is?

Upvotes: 1

Views: 818

Answers (2)

bradgonesurfing
bradgonesurfing

Reputation: 32192

I preface my answer by claiming that powershell is only for sadists. The trick is that if you put the function into a module the local variables become private and are not passed to the script block. Then to pass in the $_ variable you have to jump some more hoops.

The gv '_' get's the powershell variable $_ and passes it to the context via InvokeWithContext.

Now I know more than I ever wanted to :|

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

and as before

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

Upvotes: 1

Martin Brandl
Martin Brandl

Reputation: 58981

You could use the $global: prefix for the $name variable within your scriptblock:

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

Upvotes: 0

Related Questions