In general, you can't. The caller of a scriptblock does not have control over the SessionState associated with that scriptblock and that SessionState determines (in part) the context in which a scriptblock is executed (see the Scope section for details). Depending on where the scriptblock is defined, its SessionState might match the caller's context, but it might not.
Scope
With respect to the context in which the scriptblock is executed, there are two related considerations:
- The SessionState associated with the scriptblock.
- Whether or not the calling method adds a scope to the SessionState's scope stack.
Here is a good explanation of how this works.
The $_
Automatic Variable
$_
contains the current object in the pipeline. The scriptblock provided to %
is interpreted differently from the scriptblock provided .
and &
:
'input_object' | % {$_}
- The value of $_
is 'input_object'
because the scriptblock is bound to %
's -Process
parameter. That scriptblock is executed once for each object in the pipeline.
'input_object' | . {process{$_}}
and 'input_object' | & {process{$_}}
- The value of $_
is 'input_object'
because the $_
in the scriptblock is inside a process{}
block which executes once for each object in the pipeline.
Note that in the OP $_
was empty when the scriptblock was invoked using .
. This is because the scriptblock contained no process{}
block. Each of the statements in the scriptblock were implicitly part of the scriptblock's end{}
block. By the time an end{}
block is run, there is no longer any object in the pipeline and $_
is null.
.
vs &
vs %
.
, &
, and %
each invoke the scriptblock using the SessionState of the scriptblock with some differences according to the following table:
+---+-----------------+-----------+-------------------+----------------------+
| | Name | Kind | interprets {} as | adds scope to stack |
+---+-----------------+-----------+-------------------+----------------------+
| % | ForEach-Object | Command | Process block | No |
| . | dot-source | Operator | scriptblock | No |
| & | call | Operator | scriptblock | Yes |
+---+-----------------+-----------+-------------------+----------------------+
- The
%
command has other parameters corresponding to Begin{}
and End{}
blocks.
- For variable assignments made by the scriptblock to have side-effects on the SessionState associated with the scriptblock, use an invocation method that does not add a scope to the stack. Otherwise, the variable assignment will only affect the newly-created scope and disappear once the scriptblock completes execution.
The Most Viable Options
The two most viable options for passing the tests in OP are as follows. Note that neither invokes the scriptblock strictly in the caller's context but rather in a context using the SessionState associated with the scriptblock.
Option 1
Change the call site so that the scriptblock includes process{}
:
$modifiedLocal = 'original local value'
'input object' | SomeScriptblockInvoker {
process {
$modifiedLocal = 'modified local value'
[pscustomobject] @{
Local = $local
DollarBar = $_
}
}
}
$modifiedLocal
And invoke the scriptblock using SomeScriptblockInvoker
in OP.
Option 2
Invoke the scriptblock using %
as suggested by PetSerAl.