I fixed a bug in Start-Demo (Script attached) that is worth exploring. The heart of Start-Demo is to get a line from file, display it and then run it showing the output. To do this I used Invoke-Expression (For simplicity sakes, I’m going to show you the code assuming that the line to execute is $LINE – the actual code is a bit more complicated)
Invoke-Expression $($LINE + ” | out-host”)
We want to run the command and pipe it’s results to out-host. This “mostly” works. I noticed the failure as I started to do more complicated demos. I particular, I wanted to execute the following 2 lines:
$y = Get-Process
$y | where {$_.handles –ge 500} |sort handles
When I ran the first command, it output the processes and it didn’t assign anything to $y (can you guess why?). What was actually getting executed was:
$y = Get-Process | Out-Host
“Out-Host” took precedence over the “=” so the objects were getting printed on the host and nothing came out of the pipeline so $y was set to $NULL. S
From there I realized that I should run this in a script block and then pipe the results to “out-host”. So I changed the code to be:
Invoke-Expression $(“&{ ” + $LINE + “} | out-host”)
What do you think? Should that work or not?
Well, the first like worked a treat. It ran but did not output the process objects. Success right? Wrong! The second command didn’t work. What happens is that when you run code inside &{}, it creates a new SCOPE for variables and runs the command in that scope. So when you run:
&{$y = Get-Process} | out-host
It runs Get-Process, assigns the objects to a local variable $Y and then exits the scope – which throws away the local variable. So then when you try to run
$y | where {$_.handles –ge 500} |sort handles
$y is null so you are out of luck. With a lesser language, you’d be hosed right now but with PowerShell, we thought of these things (even if I forgot it when I wrote this script!). So how does PowerShell help? (You’ll want to put your seatbelts on for this….) You can dot-source scriptblocks!
Yup. Just as you can run a script or dot-source a script, you can run a scriptblock or dot-source it. When you run (either of these), it creates a new variable scope, executes the code, and throws away the variable scope eliminating any of the variables that were used. When you dot-source these, it runs in the current scope so any changes to variables persist after the code executes. So what is the syntax for dot-sourcing a scriptblock?
.{}
Of course.
PS> .{$y = Get-Process} | out-host
PS> .{$y |where {$_.handles -ge 500} |sort handles} | out-host
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
——- —— —– —– —– —— — ———–
510 14 18460 11640 89 10.67 940 svchost
571 18 12020 13152 98 7.81 3824 taskeng
641 26 35008 42644 176 117.69 2968 wmplayer
648 11 5832 20856 188 28.31 512 csrss
659 0 0 4592 9 4 System
659 26 29248 44148 212 45.89 1900 explorer
661 27 29812 25984 172 4.27 4268 iexplore
674 6 1708 4240 88 4.53 456 csrss
724 14 13284 15232 95 11.38 2416 CcmExec
726 14 47232 37604 213 15.48 4020 powershell
751 18 81692 81848 196 440.50 1044 svchost
765 32 11680 15452 98 34.89 1180 svchost
778 4 3024 3020 40 0.05 504 psxss
1026 29 19456 16192 124 36.36 1376 svchost
1047 33 47800 41620 293 42.59 2664 communicator
1371 14 63896 29548 229 145.56 2184 SearchIndexer
1433 19 7136 5060 57 25.31 588 lsass
1796 52 159108 96184 441 158.30 4620 iexplore
1890 50 37040 38284 157 127.00 1060 svchost
5661 83 159676 149736 781 370.58 1128 OUTLOOK
So the correct code in the script should have been:
Invoke-Expression $(“.{ ” + $LINE + “} | out-host”)
Enjoy!
Jeffrey Snover [MSFT]
Windows PowerShell/MMC Architect
Visit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
0 comments