7

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?

Scott Weinstein
  • 18,890
  • 14
  • 78
  • 115

3 Answers3

1

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
jon Z
  • 15,838
  • 1
  • 33
  • 35
  • How does this help, if I could create the data needed in the scriptblock, I wouldn't need to use a variable – Scott Weinstein Feb 06 '12 at 18:28
  • I'm not sure what the restrictions for your particular problem are, however I've updated my post with another workaround. – jon Z Feb 06 '12 at 19:03
0

I know this is an old question, however I have recently found a new work-around for this problem. It may be useful to somebody.

Use an environmental variable:

function Start-NewTab($name, [ScriptBlock]$scriptBlock)
{
    $tab = $psISE.PowerShellTabs.Add()
    $tab.DisplayName = $name
    sleep 2
    $tab.Invoke($scriptBlock)
}

$env:v = "hello world"
Start-NewTab "Test" { $env:v }
Jan Chrbolka
  • 4,184
  • 2
  • 29
  • 38
0

Or simply create the scriptblock first.

$v={"Hello world"}                                                                                                    
start-newtab "test" $v 

But you have to keep scope in mind.

Toto
  • 89,455
  • 62
  • 89
  • 125
Jeffery Hicks
  • 947
  • 5
  • 8
  • His function is valid, all he needs to do is pass the right object. All I was showing that if he passes the correct values, Start-NewTab works. But I think the real issue here is creating the scriptblock initially from variables in the current PowerShell session. – Jeffery Hicks Feb 06 '12 at 23:36
  • @ScottWeinstein Creating the script block first works. Definitely looks like a scope issue. The variable v is null inside the function when it has been defined outside of the scriptblock. – Andy Arismendi Feb 07 '12 at 04:16
  • @AndyArismendi It's not really a scoping issue (`$v` is evaluated in a new execution environment). This can be demonstrated, though, by explicitly mentioning the scope when fetching the variable. See my answer. – jon Z Feb 07 '12 at 11:51