Welcome to EMC Consulting Blogs Sign in | Join | Help

James Saull's Blog

The ethical slacker

Peculiar behaviour in PowerShell with dot sourcing and $LASTEXITCODE

Here is a script called SimpleExit.ps1

Exit 99

And here it is in use:

PS C:\temp> . .\SimpleExit.ps1 (Dot sourcing it so that variables in the script are made “global” )
PS C:\temp> $LASTEXITCODE
99 (You expected that – a variable set in the script was made “global” and therefore accessible by the calling shell/scope)

PS C:\temp> $LASTEXITCODE = 0 (Let’s reset it now)
PS C:\temp> $LASTEXITCODE
0

PS C:\temp> .\SimpleExit.ps1 (If we don’t dot source it, then $LASTEXITCODE in the script is NOT global and therefore not accessible by the calling shell/scope)
PS C:\temp> $LASTEXITCODE (so let’s see what the value of $LASTEXITCODE is now…)
99 (Yikes! So even though $LASTEXITCODE was set to 99 in a child scope it is visible in the parent – is this the magic of the Exit Keyword?)

Here is a script called MainTest.ps1

Write-Host "Started MainTest and here is the call stack:"
Get-PSCallStack
$LASTEXITCODE = 100
Write-Host "LASTEXITCODE just set to 100"
Write-Host "Now going to execute InnerTest by dot sourcing it..."
. .\InnerTest.ps1
Write-Host "Finished executing InnerTest and the LASTEXITCODE is: $LASTEXITCODE and it should be 10"
Exit 1

and a script called InnerTest.ps1:

Write-Host "Started InnerTest and here is the call stack:"
Get-PSCallStack
Write-Host "LASTEXITCODE is $LASTEXITCODE and it should be 100"
Write-Host "Setting LASTEXITCODE to 10"
Exit 10

Now observe how dot sourcing affects the behaviour of calling these scripts:


PS C:\temp> C:\temp\MainTest.ps1
Started MainTest and here is the call stack:

MainTest.ps1
prompt
LASTEXITCODE just set to 100
Now going to execute InnerTest by dot sourcing it...
Started InnerTest and here is the call stack:

.
MainTest.ps1
prompt
LASTEXITCODE is 100 and it should be 100
Setting LASTEXITCODE to 10
Finished executing InnerTest and the LASTEXITCODE is: 100 and it should be 10


PS C:\temp> . C:\temp\MainTest.ps1
Started MainTest and here is the call stack:

.
prompt
LASTEXITCODE just set to 100
Now going to execute InnerTest by dot sourcing it...
Started InnerTest and here is the call stack:
.
.
prompt
LASTEXITCODE is 100 and it should be 100
Setting LASTEXITCODE to 10
Finished executing InnerTest and the LASTEXITCODE is: 10 and it should be 10

The crux here is that when you DON’T dot source MainTest.ps1 the variable $LASTEXITCODE flows down into the child script (InnerTest.ps1) which can see the value is 100, but when it calls “Exit 10” that does not modify the value of $LASTEXITCODE in the parent script MainTest.ps1. However, when we call MainTest.ps1 by dot sourcing it then the value is modified by the child script because it is global. But if you go back to the simple example at the top you will notice that it doesn’t matter whether I dot source or not, Exit 99 modified the value $LASTEXITCODE of the root shell!

Dot sourcing is like saying “make everything in the executing script a global variable” and this would explain why if I don’t dot source MainTest.ps1 then it has its own locally scoped version of $LASTEXITCODE and why the value stays at 100. This makes it super annoying too! If I use “Exit 10” in a child script then it won’t be “bubbled up” properly because the calling script itself was not dot sourced! And it is apparently inconsistent with my first example which indicates that if the regular PowerShell shell calls a script that uses “Exit 10” it will bubble up! AGH!

This is made all the more strange by the fact that when you run the PowerShell ISE it is as if the ISE was itself dot sourced. By that I mean when you step through a script in ISE you WILL get different results to just running the script WITHOUT dot sourcing it on the command line.

Published Friday, August 07, 2009 2:29 PM by James.Saull

Comments

No Comments
Anonymous comments are disabled
Powered by Community Server (Personal Edition), by Telligent Systems