Home > other >  How does Measure-Command invoke ScriptBlock in parent scope?
How does Measure-Command invoke ScriptBlock in parent scope?

Time:07-07

In the example below after Measure-Command variable x is updated, but in example with my own version of command x remains the same.

$x = 0
Measure-Command -Expression { $x   } | Out-Null
$x # outputs 1

function Measure-Command2
{
    param([ScriptBlock]$Expression)
    . $Expression
}
$x = 0
Measure-Command2 -Expression { $x   }
$x # outputs 0

Can I use the same magic in my own functions?

CodePudding user response:

The most user-friendly solution is to define your function in a dynamic module, using
New-Module:

$null = New-Module {
  function Measure-Command2 {
    param([ScriptBlock]$Expression)
    . $Expression
  }
}

$x = 0
Measure-Command2 -Expression { $x   }
$x # outputs *1* now, as desired.

Note:

  • Modules run in their own scope domain aka session state that is separate from the caller's. Thus, unlike with non-module functions, no child scope of the caller's scope is created on invocation.

  • The script block passed as an argument, due to having been created as a literal ({ ... }) in the caller's scope, executes there, not in the function's.


Without a module, you'd need to dot-source the invocation of Measure-Command2 itself, given that functions and scripts run in a child scope by default:

function Measure-Command2 {
  param([ScriptBlock]$Expression)
  . $Expression
}
$x = 0
# Note the dot-sourcing
. Measure-Command2 -Expression { $x   }
$x # outputs *1* now, as desired.
  • Related