Anton
Anton

Reputation: 11330

Dot-sourcing functions from file to global scope inside of function

I want to import external function from file, not converting it to a module (we have hundreds of file-per-function, so treat all them as modules is overkill).

Here is code explanation. Please notice that I have some additional logic in Import-Function like adding scripts root folder and to check file existence and throw special error, to avoid this code duplication in each script which requires that kind of import.

C:\Repository\Foo.ps1:

Function Foo {
    Write-Host 'Hello world!'
}

C:\InvocationTest.ps1:

# Wrapper func
Function Import-Function ($Name) {
    # Checks and exception throwing are omitted
    . "C:\Repository\$name.ps1"

    # Foo function can be invoked in this scope
}

# Wrapped import
Import-Function -Name 'Foo'
Foo          # Exception: The term 'Foo' is not recognized

# Direct import
. "C:\Repository\Foo.ps1"
Foo          # 'Hello world!'

Is there any trick, to dot source to global scope?

Upvotes: 14

Views: 6131

Answers (4)

stijn
stijn

Reputation: 35901

Just dot-source the function as well:

. Import-Function -Name 'Foo'
Foo # Hello world!

Upvotes: 5

mjolinor
mjolinor

Reputation: 68293

You can't make the script run in a parent scope, but you can create a function in the global scope by explicitly scoping it.

Would something like this work for you?

# Wrapper func
Function Import-Function ($Path) {
    # Checks and exception throwing are omitted
    $script = Get-Content $Path
    $Script -replace '^function\s+((?!global[:]|local[:]|script[:]|private[:])[\w-]+)', 'function Global:$1'
    .([scriptblock]::Create($script))

}

The above regex only targets root functions (functions left justified; no white space to left of the word function). In order to target all functions, regardless of spacing (including sub-functions), change the $Script -replace line to:

$Script -replace '^\s*function\s+((?!global[:]|local[:]|script[:]|private[:])[\w-]+)','function Global:$1'

Upvotes: 7

briantist
briantist

Reputation: 47802

You can change the functions that are defined in the dot-sourced files so that they are defined in the global scope:

function Global:Get-SomeThing {
    # ...
}

When you dot source that from within a function, the function defined in the dot sourced file will be global. Not saying this is best idea, just another possibility.

Upvotes: 6

Frode F.
Frode F.

Reputation: 54891

I can't remember a way to run a function in global scope right now. You could do something like this:

$name = "myscript"

$myimportcode= {
    # Checks and exception throwing are omitted
    . .\$name.ps1
    # Foo function can be invoked in this scope
}

Invoke-Expression -Command $myimportcode.ToString()

When you convert the scriptblock to a string .ToString(), the variable will expand.

Upvotes: 0

Related Questions