Invoking PowerShell with complex expressions using Scriptblocks

Invoking PowerShell with complex expressions using Scriptblocks

Rate This
  • Comments 8

<WIZARD WARNING>

First a reminder:  when you are in PowerShell (formerly knows as Monad), you can run anything you want out-of-process using the construct:

PowerShell {Scriptblock}

The great example of this is


PS> #RUN EVERYTHING IN PROCESS
PS> get-process |where {$_.handles -ge 900} |sort handles

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1369      45    55328      16880   469    81.67   3056 OUTLOOK
   1575      48    84616     116200   450   103.52   3212 iexplore
   2367     190    31192      40676   152   179.70   1768 svchost


PS> #RUN EVERYTHING OUT OF PROCESS
PS> PowerShell {get-process |where {$_.handles -ge 900} |sort handles}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1369      45    55328      16880   469    81.73   3056 OUTLOOK
   1575      48    84616     116200   450   103.56   3212 iexplore
   2369     190    31192      40676   152   179.73   1768 svchost


PS> #RUN the first 2 elements of the pipeline in process and the last in-process
PS> PowerShell {get-process |where {$_.handles -ge 900}} |sort handles

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1369      45    55328      16880   469    81.75   3056 OUTLOOK
   1571      48    84768     116352   450   103.59   3212 iexplore
   2373     190    30716      40220   152   179.81   1768 svchost


PS> #RUN the first element of the pipeline in process and the last 2 in-process
PS> PowerShell {get-process} |where {$_.handles -ge 900} |sort handles

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1369      45    55328      16948   469    81.80   3056 OUTLOOK
   1567      48    84252     115892   449   103.63   3212 iexplore
   2398     190    31292      40724   153   180.02   1768 svchost

Now - have you ever tried to do that from CMD.EXE?

C:\>powershell {get-process |where {$_.handles -ge 900} | Sort Handles }
'where' is not recognized as an internal or external command,
operable program or batch file.

That doesn't work so well.  You try a number of things and depending upon what you are trying to do, that will work better or worse.  If you want to execute script blocks with single and double quotes, this becomes incomprehensible pretty quickly. 

The problem is that there are various components which try to "help" you by processing the arguments (e.g. stripping off quotes).  The reason this ALWAYS works when you invoke it from Powershell is ... we cheat.   Well, we cheat in a Kabayashi Maru sort of way (See Kirk's solution http://en.wikipedia.org/wiki/Kobayashi_Maru). 

Here is a trick that you can use to see what is going on.  The system type [ENVIRONMENT] has lots of great static properties:


PS> [Environment] |gm -static -membertype property


   TypeName: System.Environment

Name               MemberType Definition
----               ---------- ----------
CommandLine        Property   static System.String CommandLine {get;}
CurrentDirectory   Property   static System.String CurrentDirectory {get...
ExitCode           Property   static System.Int32 ExitCode {get;set;}
HasShutdownStarted Property   static System.Boolean HasShutdownStarted {...
MachineName        Property   static System.String MachineName {get;}
NewLine            Property   static System.String NewLine {get;}
OSVersion          Property   static System.OperatingSystem OSVersion {g...
ProcessorCount     Property   static System.Int32 ProcessorCount {get;}
StackTrace         Property   static System.String StackTrace {get;}
SystemDirectory    Property   static System.String SystemDirectory {get;}
TickCount          Property   static System.Int32 TickCount {get;}
UserDomainName     Property   static System.String UserDomainName {get;}
UserInteractive    Property   static System.Boolean UserInteractive {get;}
UserName           Property   static System.String UserName {get;}
Version            Property   static System.Version Version {get;}
WorkingSet         Property   static System.Int64 WorkingSet {get;}

Including the commandline used to execute the current process.

PS> [Environment]::commandline
"C:\Program Files\Windows PowerShell\v1.0\powershell.exe"

From there, you can do the following to see our cheat:

PS> PowerShell {[Environment]::CommandLine}
"C:\Program Files\Windows PowerShell\v1.0\powershell.exe" -encodedCommand WwBFAG4AdgBpAHIAbwBuAG0AZQBuAHQAXQA6ADoAQwBvAG0AbQBhAG4AZABMAGkAbgBlAA== -inputFormat xml -outputFormat xml

When we launch an external process which has a parameter with {}'s we say that that is a subshell and we TWEAK the command invocation by Base64 encoding the scriptblock and by telling the command to consume and produce XML.

If you ever need to invoke a complex piece of code directly from CMD.exe you can run PowerShell and do the following to get the string to pass to -EncodedCommand :

PS> $script = {gps |where {$_.handles -ge 900}|sort handles}.ToString()
PS> [System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetByte
s($script))
ZwBwAHMAIAB8AHcAaABlAHIAZQAgAHsAJABfAC4AaABhAG4AZABsAGUAcwAgAC0AZwBlACAAOQA
wADAAfQB8AHMAbwByAHQAIABoAGEAbgBkAGwAZQBzAA==

You can then use that string from CMD.EXE:

C:\>PowerShell -EncodedCommand ZwBwAHMAIAB8AHcAaABlAHIAZQAgAHsAJABfAC4AaABhAG4AZ
ABsAGUAcwAgAC0AZwBlACAAOQAwADAAfQB8AHMAbwByAHQAIABoAGEAbgBkAGwAZQBzAA==

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
   1366      45    55328      21552   469    83.50   3056 OUTLOOK
   1665      48    90164     122736   466   276.13   3212 iexplore
   2396     202    31036      40664   153   192.08   1768 svchost

Enjoy!
Jeffrey Snover [MSFT]
Windows PowerShell Architect

 

PSMDTAG:FAQ: What is the -EncodedCommand parameter on PowerShell.Exe for?
PSMDTAG:FAQ: How can I see the command line that started the PowerShell process?
PSMDTAG:FAQ: How do I run a scriptblock in a seperate process?
PSMDTAG:INTERNAL: avoiding funky syntax across process invocations, Base64 encoding of ENCODEDCOMMAND

Leave a Comment
  • Please add 3 and 1 and type the answer here:
  • Post
  • Why "-EncodedCommand" parameter doesn't included in "powershell -?" description?

    Is there any other hidden parameters?

    TIA



  • ah, I love a good hack; this is so dirty, it comes full-circle and is almost elegant again... :)

    - Oisin
  • Am I just dense?  I don't see any non-trivial difference in the outputs of the first three commands -- making the { } operator kind of pointless.


    And is the  { } for the command the same as the { } in the 'where' command?  Or are those different?

  • > I don't see any non-trivial difference in the outputs of the first three commands

    That's actually the point :-) In the first command, it's all run in process. In the second commmand it's all run out of process except the display. In the third example, get-process and where are run out of process and the sort is run in process. Moving the braces around controls which parts of the command line are run in proc and which are out of proc. When remoting comes in a future release, you'll be able to use (approximately) the same mechanism to run commands transparently on another machine.

    -bruce

    --
    Bruce Payette
    Windows PowerShell Technical Lead
  • As you probably know by now, &quot;remoting&quot; is one of the cornerstone features of the next version

  • PingBack from http://msdnrss.thecoderblogs.com/2008/02/29/remoting-using-powershell-v1/

  • PingBack from http://msdnrss.thecoderblogs.com/2008/02/29/remoting-using-powershell-v1-2/

  • I installed powershell, but it is not running

    How do I invoke powershell?

    I am not even sure whether it is installed?

    Msoft has poor documentation

Page 1 of 1 (8 items)