I've got a Pester It block, that looks like the below:
It "should add a header" {
{
$DifferenceObject = Get-Content -Path $PathToFile1
Set-PowershellFile -Path $PathToFile2
$ReferencedObject = Get-Content -Path $PathToFile2
Compare-Object -ReferenceObject $ReferencedObject -DifferenceObject $DifferenceObject | should -be $null
}
}
Set-PowershellFile just updates a file with some new text. I thought that the above code would do the following but I just want to confirm:
Is this correct? When I run my unit tests, the file change made in Set-PowershellFile does not persist, but the unit test passes. I assumed that wrapping this code inside the if block in {} causes the synchronous behavior but doesn't 'commit' the file changes. Is this all correct?
Mathias R. Jessen provided the crucial pointer in a comment - simply omit the inner {
and }
- but since I've seen the underlying misconception before, it's worth digging deeper:
While {... }
blocks are a regular part of most PowerShell statements, such as foreach
and if
, in isolation they do not provide a scoped block of statements that are immediately executed , the way it would work in C#, for instance.
Instead, {... }
in isolation is the literal form of a PowerShell script block , ie a block of statements for later execution, on demand , either via &
, the call operator , for execution in a child scope of , or via .
, the dot-sourcing operator , for execution directly in the scope of origin - which may or may not be the caller's scope (see caveat below).
Thus, such a script-block literal must either be saved in a variable or passed to a command expecting a script block in order to be executed later .
Absent that, a script block is implicitly output to the success output stream , which by default goes to the console (host), causing it to be rendered by its .ToString()
value, which is simply the verbatim content of the script block, excluding the delimiters ( {
and }
).
For instance:
# Script block literal is output by its *verbatim content* (without delimiters)
PS> { "Honey, I'm $HOME" }
"Honey, I'm $HOME"
In order to use a script block properly, you must execute it , typically with &
:
# `& ` executes in a child scope of and
# `. ` executes directly in the scope of origin, typically the current scope.
PS> & { "Honey, I'm $HOME" }
Honey, I'm C:\Users\jdoe # e.g.
Caveat :
Script-block literals - unlike script blocks constructed with [scriptblock]::Create('...')
- are tied to the scope domain (aka "session state") in which they are defined .
This means:
Script-block literals defined outside a module , when also called from outside a module, do run in a child scope of ( &
) / directly in ( .
) the caller's scope.
By contrast, script-block literals defined inside a module , are tied to that module's scope domain , meaning that an invocation - irrespective of where the call is made from - execute in that module's top-level scope (with .
) or a child scope thereof (with &
).
Examples:
PS> $foo = 1; function Get-ScriptBlock { { (++$foo) } }; . (Get-ScriptBlock)
2
That is, the script block created in the non-module scope domain invoked with .
ran directly in the non-module caller's scope.
PS> $foo = 1; $null = New-Module { $foo = 99; function Get-ScriptBlock { { (++$foo) } } }; . (Get-ScriptBlock)
100
That is, the script block created in the scope domain of the dynamic module created with New-Module
, invoked with .
, ran in that module's top-level scope, even though it was invoked from a non-module caller.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.