So I came across an interesting problem today while trying to dynamically load Functions using a Load-Lib function. The issue was that Loading a Script within a function means that the functions defined within that File are only defined within the Scope of the loading function. The two ways around this are to Define every Function like:
Function Global:Load-Lib { ...
Personally, I wanted my script to be the same whether I was loading it as a Global Module, or locally within a Function. In order to get the behaviour I wanted I had to redefine the functions as Global dynamically. This idea would also resolve the other issue I had: Redefining functions did not seem to always overwrite or replace the existing function code.
So now to some code…
Function Load-Lib
{
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[String]$LibraryName
)
dir "$__ProfileDir\Libraries" -recurse | ?{$_.name -match $LibraryName} | foreach {
#Clean out Functions
Unload-Lib $LibraryName
$funcbefore = Get-Item Function:
Write-Host "Loading Library $($_.Name)" -Foreground Blue;
. $_.FullName
$funcafter = Get-Item Function:
$funcafter | where { !($funcbefore -contains $_) } | %{
Write-Host $_.Name
If (Test-Path "Function:Global:$($_.Name)")
{
Remove-Item "Function:Global:$($_.Name)" -Force | Out-Null
}
If (Test-Path "Function:$($_.Name)")
{
Remove-Item "Function:$($_.Name)" -Force | Out-Null
}
#Get-Item Function:
try {
New-Item -Path Function: -Name "Global:$($_.Name)" -Value $_.ScriptBlock -Force | Out-Null
Write-Host " Reloaded function as Function:Global:$($_.Name)" -Foreground Green
} catch
{
Write-Host " Failed to Reload function as Function:Global:$($_.Name)" -Foreground Red
}
}
}
}
So in the above script I poll the defined functions before loading the script, then for each one I redefine as “Function:Global:$_”. In order for it to work properly I need to make sure that existing functions are unloaded, so I need to do some funky regex to get the functions defined in the script, and remove them each individually (either Function:Global:$_ or Function:$_ because it still shows up in the “Get-Item Function:” list without Global: (even if it is).
Function Unload-Lib
{
Param(
[Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
[String]$LibraryName
)
dir "$__ProfileDir\Libraries" -recurse | ?{$_.name -match $LibraryName} | foreach {
$txt = [io.file]::ReadAllText($_.FullName)
$funcs = ($txt.Split("`n`r") | % {$_.Trim()} | Select-String -Pattern '^(Function) (?[^ ({]*)' | %{$_.Matches[0].Groups["FName"].Value})
$funcs | % {
if (Test-Path Function:$_) {
Write-Host " Removing Function:$_" -Foreground Magenta
Remove-Item Function:$_ -Force | Out-Null
}
if (Test-Path Function:Global:$_) {
Write-Host " Removing Function:Global:$_" -Foreground Magenta
Remove-Item Function:Global:$_ -Force | Out-Null
}
}
}
}
There are a few more things I’ve found out, I’ll try and get up sometime in the next few days.
Cheers, Chris.