I'm trying to automate creation of a bunch of tabs in PowerShell ISE
I've started with a function such as
function Start-NewTab($name, [ScriptBlock]$scriptBlock)
{
$tab = $psISE.PowerShellTabs.Add()
$tab.DisplayName = $name
sleep 2
$tab.Invoke($scriptBlock)
}
however when I run it like so
$v = "hello world"
Start-NewTab "Test" { $v }
hello world
isn't shown, unlike the following fragement
function Test-ScriptBlock([ScriptBlock]$sb) { & $sb }
Test-ScriptBlock { $v }
What's going on here and how do I fix it?
To close a tab, you can use any of the following techniques: 1 Click the tab that you want to close. 2 On the File menu, click Close PowerShell Tab, or click the Close button ( X) on an active tab to close the tab. More ...
A script block is an instance of a Microsoft.NET Framework type System.Management.Automation.ScriptBlock. Commands can have script block parameter values. For example, the Invoke-Command cmdlet has a ScriptBlock parameter that takes a script block value, as shown in this example:
Like a function, Begin process and End can be added to a scriptblock. The begin block is used to define variables, path etc. The process block contains the code for manipulation. End block is cleaning up the code. $username = { Write-Host "my name is $ ($args [0])..."
On the File menu, click New PowerShell Tab. The new PowerShell tab always opens as the active window. PowerShell tabs are incrementally numbered in the order that they are opened. Each tab is associated with its own Windows PowerShell console window.
A "Tab" container is equated to a runspace (or powershell execution environment) in the ISE. Since you are creating a new Tab (i.e. powershell execution environment) the variable v is undefined in that execution environment. The scriptblock is evaluated in the new execution environment and outputs the value of v (nothing).
It's easy to see how variable resolutions differs in the case of Test-Scriptblock from the case of Start-NewTab if you try to get the variable in the scriptblock by explicitly mentioning the scope where the variable should be found.
PS>Test-ScriptBlock { get-variable v -scope 0}
Get-Variable : Cannot find a variable with name 'v'.
PS>Test-ScriptBlock { get-variable v -scope 1}
Get-Variable : Cannot find a variable with name 'v'.
PS>Test-ScriptBlock { get-variable v -scope 2} # Variable found in grandparent scope (global in the same execution environment)
Name Value
---- -----
v hello world
PS>Start-NewTab "Test" { get-variable v -scope 0 } # global scope of new execution environment
Get-Variable : Cannot find a variable with name 'v'.
PS>Start-NewTab "Test" { get-variable v -scope 1 } # proof that scope 0 = global scope
Get-Variable : The scope number '1' exceeds the number of active scopes.
One workaround for your problem is to define your variable in the scriptblock:
Start-NewTab "Test" { $v = "hello world";$v }
Edit: One more thing, your title mentions 'closure'. Scriptblocks in Powershell are not closures, however you can create a closure from a scriptblock. This won't help you with the problem you describe, though.
Edit2: Another workaround:
$v = "hello world"
Invoke-Expression "`$script = { '$v' }"
Start-NewTab "test" $script
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With