Search code examples
functionpowershellvariablesscope

Variables don't seem to be working inside of the function in PowerShell


I have a logging function in my script however when I call the function it seems to work properly except for the actual logging portion. It echos to the screen just fine, but when I go to check the log file it doesn't update. Below is the code.

Function Get-Logger { 
    param(
       [Parameter(Mandatory=$true)]
       [String]$message,
       [Parameter(Mandatory=$false)]
       [validatescript({[enum]::getvalues([system.consolecolor]) -contains $_})][string]$Color
    )

    ### Function Variables ###
    $Log = $MyInvocation.MyCommand.ToString().Replace(".ps1",".log")
    $LogPath = "C:\Source\Scripts\ScriptLog" 
    $LogFile = "$LogPath\$Log" 
    $TimeStamp = Get-Date -Format "MM-dd-yyy hh:mm:ss"

    #Creates the ScriptLog folder in C:\Source\Scripts if it doesn't exist
    IF(-not(Test-Path $LogPath)) {
        New-Item -ItemType Directory -Path $LogPath
        Get-Logger "Created ScriptLog Directory"
    } 

    Write-Host $TimeStamp -NoNewline
    IF ($Color) {
        Write-Host `t $message -ForegroundColor $($color)
    } Else {
        Write-Host `t $message -ForegroundColor Cyan
        }       

    $logMessage = "[$TimeStamp]  $message"
    $logMessage | Out-File -Append -LiteralPath $LogFile


}

The variables are only used within the function, if I place the variables outside of the function it works, but I want to try and keep the variables within the function itself.

I've tried adding scopes as well $global:, $script:, $local:, etc.. and none of that seems to work. Is it possible to actually have the variables stay within the function, or do I have to place them outside the function?


Solution

  • Local variables are always visible in the same scope (and all descendant scopes, unless created with the $private: scope specifier), so they aren't the problem.

    However, it looks like your intent is to base the $Log variable value on the script-level definition of $MyInvocation, not the function's (which reflects how the function was invoked).

    To refer to the script's definition of $MyInvocation - from which you can derive the script file name as intended - use the $script: scope specifier:

    $Log = $script:MyInvocation.MyCommand.ToString().Replace(".ps1",".log")
    

    More simply, however, you can use the automatic $PSCommandPath variable, which contains the script's full file path, even inside functions:

    $Log = [IO.Path]::GetFileNameWithoutExtension($PSCommandPath) + '.log'
    

    In PowerShell (Core) 7, you can alternatively use Split-Path -LeafBase (not supported in Windows PowerShell):

    $Log = (Split-Path -LeafBase $PSCommandPath) + '.log'