Start-Demo: Help doing demos using PowerShell

Start-Demo: Help doing demos using PowerShell

  • Comments 15

A couple a weeks ago I got a call from BillG's Technical Assistant (TA) telling me that Bill and Ray (Ozzie) wanted to get a demo of the new stuff we were doing with PowerShell. The setting was Bill's conference room that would be organized with a number of Demo stations. You'd get called in and were allowed to bring one other person but then it would be Bill and Ray sitting next to you getting the demo – so you pretty much have their full attention. That's great except for the fact that I am a bad typist and make mistakes all the time. That is why PowerShell supports wildcards, parameter disambiguation and tab completion – you benefit from my flaws. J Still, this was all the new stuff and had never been demo (to anyone) before and wasn't even to ready for alpha testing so I was taking a risk in showing it.

In the past, I dealt with this sort of situation by having a text file which contained all the things I wanted to type in it. Doing a cut-n-paste from notepad during a demo looks REALLY bad so what I would do is to paste the entire sequence into a PowerShell session. This would execute all the commands and then I would use the CMD.EXE command history feature and use the UP arrow to get back to the first command and then just type ENTER and DOWNARROW to rerun the demo one command at a time. This allows you to focus on WHAT you want to say instead of putting all your mental energy into typing things correctly. This is what I did when I did a demo at Bob Muglia's keynote address in Barcelona last year.

Let me be clear on this point - this technique doesn't work very well! It's a toss up as to whether it is better or worse they just typing things in from scratch. The big problem in both Barcelona and in the BillG/RayO demo was STATE. When you run a command, it can change the state of the system so you might not be able to just go to the top of a demo and run it all over again because the state might be changed. The other issue is that the CMD.exe mechanism is pretty fragile. If you change the command in any way shape or form, it decides that it is a new command and thus it resets your position in the command history to be at the end. ARRRGGG! I'll spare you the horror stories and get to the punch line. I decided I had had enough of that nonsense and decided to write a script which would run a demo for me. I've called this function: Start-Demo. I've attached it to this blog entry. I have to say that I'm pretty please with how it came out.

I strongly encourage you to use this if you ever have to give a demo – it totally rocks! It completely transforms the experience of giving a demo, allowing you to focus on your messages instead of typing. It is REAL in the sense that the commands REALLY run, the script just eliminates your typing. I put in all sort's of "grace notes" including:

  • You can specify which file you want to demo (it defaults to ".\demo.txt")
  • You can specify which command to start with (it defaults to 0)
  • It shows you the command (both at the prompt and in the Window Title [for the folks at the back of the room) and waits for input. If your input is <CR>, it runs the command.
  • You can provide other input and it will do other actions. You can:
    • Ask for help using "?"
    • Quit at any point
    • Dump the list of commands in the demo. It produces a red line above your current point in the demo
    • Run another command or Suspend the demo and enter into a nested prompt to explore a topic
    • Go to a specified command in the demo
    • Find all the commands in the demo using a regular expression
    • Check your time. It displays how many minutes and seconds since the start of the demo. This information is also displayed in the Window title on an ongoing basis.

    I think you'll find some pretty useful techniques in the script itself so even if you do use it for your demos, you might want to review the script:

    function Start-Demo
    {
        param($file=".\demo.txt", [int]$command=0)
        Clear-Host

        $_lines = Get-Content $file
        $_starttime = [DateTime]::now
        Write-Host -for Yellow "<Demo [$file] Started>"


        # We use a FOR and an INDEX ($_i) instead of a FOREACH because
        # it is possible to start at a different location and/or jump
        # around in the order.
        for ($_i = $Command; $_i -lt $_lines.count; $_i++)
        {    
            $_SimulatedLine = $("`n[$_i]PS> " + $($_Lines[$_i]))
            Write-Host -NoNewLine $_SimulatedLine

            # Put the current command in the Window Title along with the demo duration
            $_Duration = [DateTime]::Now - $_StartTime
            $Host.UI.RawUI.WindowTitle = "[{0}m, {1}s]        {2}" -f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds, $($_Lines[$_i])
            if ($_lines[$_i].StartsWith("#"))
            {
                continue
            }
            $_input=[System.Console]::ReadLine()
            switch ($_input)
            {
                "?"    
                        {
                            Write-Host -ForeGroundColor Yellow "Running demo: $file`n(q) Quit (!) Suspend (#x) Goto Command #x (fx) Find cmds using X`n(t) Timecheck (s) Skip (d) Dump demo"
                            $_i -= 1
                        }
                "q"    
                        {
                            Write-Host -ForeGroundColor Yellow "<Quit demo>"
                            return                    
                        }
                "s"
                        {
                            Write-Host -ForeGroundColor Yellow "<Skipping Cmd>"
                        }
                "d"
                        {
                            for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
                            {
                                 if ($_i -eq $_ni)
                                 {     Write-Host -ForeGroundColor Red ("*" * 80)
                                 }
                                 Write-Host -ForeGroundColor Yellow ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
                            }
                            $_i -= 1
                        }
                "t"    
                        {
                             $_Duration = [DateTime]::Now - $_StartTime
                             Write-Host -ForeGroundColor Yellow $("Demo has run {0} Minutes and {1} Seconds" -f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds)
                             $_i -= 1
                        }
                {$_.StartsWith("f")}
                        {
                            for ($_ni = 0; $_ni -lt $_lines.Count; $_ni++)
                            {
                                 if ($_lines[$_ni] -match $_.SubString(1))
                                 {
                                        Write-Host -ForeGroundColor Yellow ("[{0,2}] {1}" -f $_ni, $_lines[$_ni])
                                 }
                            }
                            $_i -= 1
                        }
                {$_.StartsWith("!")}
                        {
                             if ($_.Length -eq 1)
                             {
                                     Write-Host -ForeGroundColor Yellow "<Suspended demo - type 'Exit' to resume>"
                                     $host.EnterNestedPrompt()
                             }else
                             {
                                     trap [System.Exception] {Write-Error $_;continue;}
                                     Invoke-Expression $($_.SubString(1) + "| out-host")
                             }
                             $_i -= 1
                        }
                {$_.StartsWith("#")}    
                        {
                             $_i = [int]($_.SubString(1)) - 1
                             continue
                        }
                default
                        {
                             trap [System.Exception] {Write-Error $_;continue;}
                             Invoke-Expression $($_lines[$_i] + "| out-host")
                             $_Duration = [DateTime]::Now - $_StartTime
                             $Host.UI.RawUI.WindowTitle = "[{0}m, {1}s]        {2}" -f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds, $($_Lines[$_i])
                             [System.Console]::ReadLine()
                        }
            }
        }
        $_Duration = [DateTime]::Now - $_StartTime
        Write-Host -ForeGroundColor Yellow $("<Demo Complete {0} Minutes and {1} Seconds>" -f [int]$_Duration.TotalMinutes, [int]$_Duration.Seconds)
        Write-Host -ForeGroundColor Yellow $([DateTime]::now)
    }

Let's see it in action.  NOTE - I've gone back and colored my INPUT red so that you can see it more clearly.  It will just be normal when you run it. 


PS> Start-Demo
<Demo [.\demo.txt] Started>

[0]PS> # ****** PowerShell is a SHELL *****
[1]PS> hostname?
Running demo: .\demo.txt
(q) Quit (!) Suspend (#x) Goto Command #x (fx) Find cmds using X
(t) Timecheck (s) Skip (d) Dump demo

[1]PS> hostname
jpsvista1



[2]PS> net use
New connections will be remembered.


Status Local Remote Network

-------------------------------------------------------------------------------
Unavailable Y: \\axp-test\builds\vistartm
Microsoft Windows Network
Unavailable Z: \\powershell\public Microsoft Windows Network
The command completed successfully.




[3]PS> notepad#2

[2]PS> net useS
<Skipping Cmd>

[3]PS> notepadd
[ 0] # ****** PowerShell is a SHELL *****
[ 1] hostname
[ 2] net use
********************************************************************************
[ 3] notepad
[ 4] # ******* Structured Commands ******
[ 5] Get-Process -ProcessName lsass
[ 6] gps -p lsass
[ 7] gps l*s
[ 8] gps |Where {$_.Handles -ge 500} |Sort Handles |ft Handles,Name,Desc* -Auto
[ 9] gps [b-t]*[c-r] |stop-process -whatif

[3]PS> notepad#6

[6]PS> gps -p lsassT
Demo has run 1 Minutes and 52 Seconds

[6]PS> gps -p lsass

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
1377 21 8872 5168 71 111.97 592 lsass





[7]PS> gps l*s!GPS *S

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
755 6 2276 3844 91 13.38 456 csrss
762 13 7076 23340 211 291.06 516 csrss
1369 20 8872 5168 71 111.97 592 lsass
778 4 3020 2920 40 0.05 508 psxss
299 8 5416 6744 43 20.14 568 services
32 1 248 536 4 0.11 388 smss



[7]PS> gps l*s!
<Suspended demo - type 'Exit' to resume>
PS> gps c*s

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
751 6 2276 3844 91 13.38 456 csrss
760 13 7076 23340 211 291.19 516 csrss


PS> exit

[7]PS> gps l*s

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
1369 20 8872 5168 71 111.97 592 lsass





[8]PS> gps |Where {$_.Handles -ge 500} |Sort Handles |ft Handles,Name,Desc* -AutoQ
<Quit demo>

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

Attachment: Start-Demo.ps1
Leave a Comment
  • Please add 2 and 5 and type the answer here:
  • Post
  • A keeper!  This is terrific stuff, please keep it coming.

  • Very nice, thanks!

    This will be handy for those of us that are doing demos to try to convince the "higher ups" that we need to build our administrative apps on Powershell (ala Exchange). :)

  • Very nice but you didn't tell us what Bill and Ray thought about the demo.  :-)

  • > Very nice but you didn't tell us what Bill and Ray thought about the demo.  :-)

    My badge still works.  :-)

    jps

  • I have done demos using the cut and paste method and it does look clumsy but it is better than trying to type - especially long lines.

    This is a superb idea and I'll be trying it out later in the month

    This is going to make life much easier for demos - thanks for sharing

  • freakin amazing stuff Jeffrey!!

    i hope the next version of vista just boots straight into powershell, with no other gui at all.

  • During my webcast on 5/3/2007 I used jeffrey's start-demo script to run my powershell demos. Lot of folks

  • Great script. But it doesn't seem to work for my variables that I am using in my demo...

    Try this in your demo.txt:

    $f = dir *.*

    $f[0]

    I changed this line, and things seem to work better for me.

    ...

    Invoke-Expression $($_lines[$_i] + "| out-host")

    ...

    Invoke-Expression $("(" + $_lines[$_i] + ")" + "| out-host")

    ...

    Thanks for the great site,

    - Joe

  • Tried to run but no result come out...(No error and just a return) please help

  • So embarrassed to ask, but start-demo doesn't run in POwershell for me and I am sure there is a simple thing to do to make it happen. I tried including it in the v1.0 directory and it will run, but just leaves me with a normal powershell prompt. Do we put it in a profile or put it in a directory. How do I use this amazing tool?

    -Gordon - Toronto

  • Jeffry Snover and many other have used Start-Demo in powershell.exe http://blogs.msdn.com/powershell/archive/2007/03/03/start-demo-help-doing-demos-using-powershell.aspx

  • Jeffry Snover and many other have used Start-Demo in powershell.exe http://blogs.msdn.com/powershell

  • Jeffry Snover and many other have used Start-Demo in powershell.exe http://blogs.msdn.com/powershell

  • Jeffry Snover and many other have used Start-Demo in powershell.exe http://blogs.msdn.com/powershell

Page 1 of 1 (15 items)