Dwarfsoft [GPA]

PowerShell Profile and Global Functions

by on Jul.16, 2012, under Scripting, Work


Print This Post Print This Post

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) (?<FName>[^ ({]*)' | %{$_.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.

:,

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!