0
$logDate = Get-Date
$codeBlockId = "NOTHING"
$addToCodeRunLog = Write-Host "However, inside addToCodeRunLog, codeBlockId is $codeBlockId. This line comes second"

function addCodeBlockLogs {
Write-Host "inside CodeBlockLogs, the value of codeBlockID is $codeBlockId. This line comes first."
$addToCodeRunLog
}

function deviceInfoLog {
$codeBlockId = "1002"
addCodeBlockLogs
}

deviceInfoLog

Returns:

However, inside addToCodeRunLog, codeBlockId is NOTHING. This line comes second
inside CodeBlockLogs, the value of codeBlockID is 1002. This line comes first.
  1. Why does the value of $codeBlockID not get changed when $addCodeToRunLog is called from the function?
  2. Why does is return the value of $addCodeToRunLog before returning the Write-Host value of the previous line?

2 Answers2

2
  1. Why does is return the value of $addCodeToRunLog before returning the Write-Host value of the previous line?

Because you're mistakenly using Write-Host in your assignment:

# Write-Host prints *to the display*, it doesn't output to the *pipeline*.
# Therefore:
#  * The message *prints instantly*
#  * *Nothing* is stored in var. $addToCodeRunLog, because Write-Host
#    produced *no success-output stream (pipeline) output*.
$addToCodeRunLog = Write-Host "However, inside addToCodeRunLog, codeBlockId is $codeBlockId. This line comes second"

See this answer for background information.

To instead store the string in your $addToCodeRunLog variable, either replace Write-Host with Write-Output or, preferably, just assign the string directly:

$addToCodeRunLog = "However, inside addToCodeRunLog, codeBlockId is $codeBlockId. This line comes second"

However, this will still not do what you want, as discussed next.


  1. Why does the value of $codeBlockID not get changed when $addCodeToRunLog is called from the function?

Even if you fix your assignment as shown above, it uses the value of variable $codeBlockId at the point when the assignment statement containing the "..." string is executed, and "bakes it into" the string - that is how expandable (double-quoted) string ("...") work in PowerShell.

If you want to defer expansion (string interpolation) for on-demand interpolation based on the then-current variable value(s), you need to :

  • Either: Use string templating, where you define your string as a verbatim (single-quoted) string ('...') and later call $ExecutionContext.InvokeCommand.ExpandString() with it - see this answer (also shows an alternative with -f).

  • Or: You can wrap your expandable string in a function - as shown in your own answer - which implicitly also defers expansion.

    • PowerShell's dynamic scoping ensures that the child scope in which your wrapper function executes sees the desired $codeBlockId value from its parent scope - see the conceptual about_Scopes help topic.

    • Note: A variant solution based on the same principles would be to define your variable as a script block ({ ... }), which you can invoke on demand with &, the call operator, in order to perform on-demand expansion:

      $addToCodeRunLog = { "`$foo = $foo" }
      $foo = 1
      & $addToCodeRunLog # -> '$foo = 1'
      $foo = 2
      & $addToCodeRunLog # -> '$foo = 2'
      
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Thanks. I actually modified the code to include Write-Host as a way to simplify it for SO, but I suspect the same rules apply to the original. Thank you for your comprehensive answer. Very useful! – RapidScampi Mar 20 '23 at 18:44
1

I believe it's because $addCodeToRunLog is declared as a variable rather than a function. I fixed it by changing it to:

$logDate = Get-Date
$codeBlockId = "NOTHING"
function addToCodeRunLog {
 Write-Host "However, inside addToCodeRunLog, codeBlockId is $codeBlockId. This line comes second"
}

function addCodeBlockLogs {
Write-Host "inside CodeBlockLogs, the value of codeBlockID is $codeBlockId. This line comes first."
addToCodeRunLog
}

function deviceInfoLog {
$codeBlockId = "1002"
addCodeBlockLogs
}

deviceInfoLog
  • 1
    I've restructured my answer to directly address your two questions. Using a function indeed avoids the problem, because it _defers_ evaluation of the expandable string (`"..."`) until the function is called, using the then-current variable value. The answer to the second question is that you mistakenly used `Write-Host` in your variable assignment, which prints immediately to the display and stores nothing in the variable. – mklement0 Mar 17 '23 at 22:47