简体   繁体   中英

Powershell running a scriptblock - scope, dot-sourcing

I want to write a function that accepts a scriptblock as a parameter and executes that scriptblock in the scope in which it was invoked.

The Measure-Command is an example of the behavior I would like. The scriptblock runs in with the same scope as the Measure-Command itself. If the scriptblock references a variable in this scope, the script can change it.

Attached is a sample scriptblock that increments the $a variable. When invoked by Measure-Command, the variable is incremented. But when invoked by the Wrapper functions, the variable will not increment -- unless I dot-source both the invocation of the Wrapper function and the Wrapper function itself uses dot-sourcing.

function Wrapper1
{
    param( $scriptBlock )
    $startTime = Get-Date
    Write-Output ( "{0:HH:mm:ss} Start script" -f $startTime )
    & $scriptBlock
    $endTime = Get-Date
    Write-Output ( "{0:HH:mm:ss} End script - {1:c} seconds elapsed" -f $endTime, ( $endTime - $StartTime ) )
}

function Wrapper2
{
    param( $scriptBlock )
    $startTime = Get-Date
    Write-Output ( "{0:HH:mm:ss} Start script" -f $startTime )
    . $scriptBlock
    $endTime = Get-Date
    Write-Output ( "{0:HH:mm:ss} End script - {1:c} seconds elapsed" -f $endTime, ( $endTime - $StartTime ) )
}

$a = 1
Write-Output "Initial state: `$a = $a"

Measure-Command { $a++ } | Out-Null
Write-Output "Measure-Command results: `$a = $a"

Wrapper1 { $a++ }
Write-Output "Wrapper1 results: `$a = $a"

. Wrapper1 { $a++ }
Write-Output "dot-sourced Wrapper1 results: `$a = $a"

Wrapper2 { $a++ }
Write-Output "Wrapper2 results: `$a = $a"

. Wrapper2 { $a++ }
Write-Output "dot-sourced Wrapper2 results: `$a = $a"

The result of running this code is:

Initial state: $a = 1
Measure-Command results: $a = 2
13:44:49 Start script
13:44:49 End script - 00:00:00 seconds elapsed
Wrapper1 results: $a = 2
13:44:49 Start script
13:44:49 End script - 00:00:00.0157407 seconds elapsed
dot-sourced Wrapper1 results: $a = 2
13:44:49 Start script
13:44:49 End script - 00:00:00 seconds elapsed
Wrapper2 results: $a = 2
13:44:49 Start script
13:44:49 End script - 00:00:00 seconds elapsed
dot-sourced Wrapper2 results: $a = 3

Although this last option works, I'd like to avoid the dot-source syntax invoking Wrapper2. Is this possible? The Measure-Command doesn't use the dot-source syntax so it seems that it would be possible.

PetSerAl , as he is wont to do, has provided the crucial pointer in a terse comment on the question:

Putting the function in a module , along with dot-sourced invocation of the script-block argument, solves the problem:

$null = New-Module {
  function Wrapper {
    param($ScriptBlock)
    . $ScriptBlock
  }
}

$a = 1
Wrapper { $a++ }

$a

The above yields 2 , proving that the script block executed in the caller's scope.

For an explanation of why this works and why it is necessary, see this answer to a related question.

Note : The above approach doesn't extend to pipeline use, where you'll want to pass script blocks that expect to use automatic variable $_ to reference the object at hand (eg,
1, 2, 3 | Wrapper { $_ ... } 1, 2, 3 | Wrapper { $_ ... } ; to support this use case, a workaround is needed - see this answer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM