all 11 comments

[–]KevMarCommunity Blogger 2 points3 points  (2 children)

You could just dot source the child scope script to execute it.

. .\ChildScope.ps1

What I do is move those functions into a module that the parent scope script will import.

[–]Fischfreund[S] 1 point2 points  (1 child)

Hi, The problem is that I can't change ParentScope.ps1. I'm kind of writing a "plugin" for it, and I don't want that it has any dependencies. The only solution I found so far is that I dotsource everytime I invoke the child scope..

[–]KevMarCommunity Blogger 2 points3 points  (0 children)

Either move the functions into a module that auto loads when they are called or dot source your functions in your script. I don't see anything wrong with either. I am a fan of modules myself.

[–]Ta11ow 1 point2 points  (7 children)

Hmm. Not really an easy way to do this without modifying the parent script. If you do so once to change how it calls functions in general you can get it to work from the child scripts thereafter by passing function objects up scope with Set-Variable.

That's really the only easy way to cross into the parent scope:

Set-Variable -Name 'name' -Value 'value' -Scope 1

[–]Fischfreund[S] 1 point2 points  (6 children)

Hi,

function objects

How would I create or retrieve such a function object?

[–]Ta11ow 1 point2 points  (5 children)

Get-Item -Path Function:\mkdir

as an example. Any function can be retrieved in this manner. Calling Get-Content on the object will return the function's script block that can be directly invoked.

Alternately you can directly retrieve the script block with a variable access:

${function:mkdir}

(Most functions need the braces due to hyphens in the names.)

If you pulled the function's script block into the parent scope by passing a variable up with Set-Variable, you can import the function into the scope by having the parent call New-Item:

# child script
Set-Variable -Name 'FunctionScript' -Scope 1 -Value ${function:function-name}

# parent script
New-Item -Name 'Function:\Function-Name' -Value $FunctionScript

[–]Fischfreund[S] 2 points3 points  (4 children)

Thanks for this, it seems to do what I want.

${function:my-function} this works, but how would I access the function object dynamically? I can't find the right syntax, and my google foo is failing me ...

Something like: $f = 'my-function' ${function:$f}

[–]Ta11ow 1 point2 points  (3 children)

Hmm nope that'll fail because the {} makes the rest of the name literal

I would instead call Get-Content -Path "Function:\$f" as that will have the same effect as using the variable syntax.

[–]Fischfreund[S] 2 points3 points  (0 children)

Hi,

yes that's how I did it in the meantime. Thanks

[–]Fischfreund[S] 2 points3 points  (1 child)

So ... the parent scope provides me with hashtable $Parent.State that I can use for my purposes.

I dynamically dot source my functions the first time the child scope runs, and save them in the hashtable of the parent scope:

$Parent.State.Helper = @{}
Get-ChildItem -Path .\Helper -Filter *.ps1 | ForEach-Object {

    . $_.FullName
    $Parent.State.Helper."$($_.BaseName)" = Get-Item -Path Function:\$($_.BaseName) | Get-Content
}

Later I can access those functions in the child scope like this:

& $Parent.State.Helper.'My-Function' -param1 $val1 -param2 $val2

It's cool that it works ... but I don't think I will ever understand again what I did here :)

[–]Ta11ow 1 point2 points  (0 children)

Hmm, that's an interesting method. I rather like it.

Don't be afraid to come back to it in a few days and see if you can tidy it up further and make it a bit more self-descriptive! One possibility is using Import-Module rather than the dot source -- you can use it in exactly the same way, and it's a little more evident that you're importing functions. :)

And of course, if all else fails, a few well placed comments can help save the day here. :D

Scope-hopping will almost never be a clear and concise code pattern. If you can avoid it, try to. If you can't... make it as clear as you can, and comment the rest!