You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add API support for dot sourcing a ScriptBlock #3581
It is currently not possible to implement a cmdlet like ForEach-Object or Measure-Command outside of PowerShell because there is no api support for invoking a script block instance in the callers scope - or dot sourcing.
The workaround is quite expensive - it looks something like:
Have you looked at $executioncontext.InvokeCommand.InvokeScript($false, {$x=3.14}, @(), @())
One of the signatures for InvokeScript is System.Collections.ObjectModel.Collection[psobject] InvokeScript(bool useLocalScope, scriptblock scriptBlock, System.Collections.IList input, Params System.Object[] args)
This should do what you want.
It looks like that api covers most scenarios - but I vaguely recall a scenario, maybe LINQ filter expressions or something - where you have a script block, but maybe not have easy access to the execution context.
What said InvokeScript overload doesn't cover is the ability to relay the current pipeline input object via automatic variable $_, which is what's needed to be on par with how cmdlets such as Where-Object and ForEach-Object invoke script-block arguments.
PetSerAl came up with a robust workaround, but it's cumbersome and far from obvious: it uses an aux. ForEach-Object call to invoke the script block with the object to bind to $_ passed via -InputObject.
Here's an example of what currently works, but it would certainly be nice to have a more direct way of achieving this:
Add-Type@' using System.Management.Automation; [Cmdlet("Get", "Foo")] public class GetFooCmdlet : PSCmdlet { [Parameter(ValueFromPipeline=true)] public object InputObject { get; set; } [Parameter(Position=0)] public ScriptBlock ScriptBlock { get; set; } private PowerShell _ps = null; protected override void BeginProcessing() { // Create a PS instance in the current runspace with a ForEach-Object invocation. _ps = PowerShell.Create(RunspaceMode.CurrentRunspace).AddCommand("ForEach-Object"); } protected override void ProcessRecord() { _ps.Commands.Commands[0].Parameters.Clear(); // clear previous parameters // Add the script block and the current pipeline input object as parameters for the ForEach-Object call. // ForEach-Object takes care of setting $_ in the scope of the target script block (passed to -Process), via // -InputObject. WriteObject( _ps.AddParameter("Process", ScriptBlock).AddParameter("InputObject", InputObject).Invoke() ); } }'@-PassThru |% Assembly |Import-Module# Define a local variable.$var=42# Invoke the cmdlet with pipeline input and a scriptblock that references the# latter as well as local variable $var@{ Name='hi' },@{ Name='ho' } |get-foo { $_.Name; ++$var } # -> 'hi', 'ho'# See if $var was changed.$var# -> 44 - successfully modified, because the scriptblock was "dot-sourced".
It's actually easier to do this in PowerShell code, given that you can call ForEach-Object directly there - see this SO answer.
By contrast, it's easier to invoke a script block in a child scope of the origin scope with pipeline input relayed as $_, the way that delay-bind script blocks and script blocks inside calculated properties do )though it's unclear if that is actually by design) - see next comment.
Invoking a caller-scope-bound script block in a child scope - as delay-bind script blocks and calculated properties do - with the current pipeline input object relayed as $_ looks like this (using PS code, with the script block stored in $ScriptBock, and the current pipeline input object in $InputObject):
$ScriptBlock.InvokeWithContext(
$null,# extra functions to define
[psvariable]::new('_',$InputObject) # define $_
)
To do the same directly in the caller's scope - as Where-Object and ForEach-Object do - we'd need a new .InvokeWithContext() overload that has a useLocalScope parameter too, as .InvokeScript() already does.
$ScriptBlock.InvokeWithContext(
$false,# WISHFUL THINKING: opt into not creating a child scope.$null,# extra functions to define
[psvariable]::new('_',$InputObject) # define $_
)
Reacted by sharpchen
microsoft-github-policy-service commented on Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commented on Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commented on Nov 23, 2023
Activity
BrucePay commentedon Apr 18, 2017
Have you looked at
$executioncontext.InvokeCommand.InvokeScript($false, {$x=3.14}, @(), @())One of the signatures for InvokeScript is
System.Collections.ObjectModel.Collection[psobject] InvokeScript(bool useLocalScope, scriptblock scriptBlock, System.Collections.IList input, Params System.Object[] args)This should do what you want.
BrucePay commentedon Apr 18, 2017
Removed the enhancement/Up-for-grabs tags as there is already an API that does this.
lzybkr commentedon Apr 18, 2017
It looks like that api covers most scenarios - but I vaguely recall a scenario, maybe LINQ filter expressions or something - where you have a script block, but maybe not have easy access to the execution context.
mklement0 commentedon Nov 18, 2018
What said
InvokeScriptoverload doesn't cover is the ability to relay the current pipeline input object via automatic variable$_, which is what's needed to be on par with how cmdlets such asWhere-ObjectandForEach-Objectinvoke script-block arguments.PetSerAl came up with a robust workaround, but it's cumbersome and far from obvious: it uses an aux.
ForEach-Objectcall to invoke the script block with the object to bind to$_passed via-InputObject.Here's an example of what currently works, but it would certainly be nice to have a more direct way of achieving this:
It's actually easier to do this in PowerShell code, given that you can call
ForEach-Objectdirectly there - see this SO answer.By contrast, it's easier to invoke a script block in a child scope of the origin scope with pipeline input relayed as
$_, the way that delay-bind script blocks and script blocks inside calculated properties do )though it's unclear if that is actually by design) - see next comment.mklement0 commentedon Jan 23, 2019
Invoking a caller-scope-bound script block in a child scope - as delay-bind script blocks and calculated properties do - with the current pipeline input object relayed as
$_looks like this (using PS code, with the script block stored in$ScriptBock, and the current pipeline input object in$InputObject):To do the same directly in the caller's scope - as
Where-ObjectandForEach-Objectdo - we'd need a new.InvokeWithContext()overload that has auseLocalScopeparameter too, as.InvokeScript()already does.microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 23, 2023
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.