Showing posts with label prompt. Show all posts
Showing posts with label prompt. Show all posts

Monday, March 10, 2008

Custom PowerShell prompt

 

If you are used to measuring commands in PowerShell then this custom prompt can be useful for you. Instead of measuring commands manually (with measure-command), the custom prompt code (below, comments inline) automates the measuring process and presents three bits of information at the console title. Here is how it looks:

console

 

  1. The first part (c:\scripts) shows the current working directory.
  2. The second part (Session Execution Time: 00:12:15) displays, in HH:mm:ss format, the total amount of time it took PowerShell to execute all commands since the console had started.
  3. The last part (Last [00:01.546]) displays how long it took the last command to be completed, in mm:ss.millisecond format.

The last part of the prompt (PS 3>) shows the ID of the last executed command, you ca use the ID to invoke commands, instead of retyping them, from the commands history by passing the command ID, see the help for get-history.

To use the prompt, copy the code below and paste it into your $profile, restart PowerShell and type some commands, you'll see the console title changes.

 

***UPDATE # 1 ***
Already found a bug. Repeatedly pressing the ENTER key causes the global $pshUse to add-up the last command execution time. The code below includes the fix.


***UPDATE # 2 ***

Added support for $NestedPromptLevel visualization. Thanks Andy!
Each nested prompt level is indicated by an extra ">"

 

# global variable to hold total (cumulative) execution time
$global:pshUse = New-TimeSpan
 
# holds the last command id
$global:lasdID
 
function Prompt{ 
    
    # get the last command
    $item = Get-History -Count 1
    
    # don't add-up times when typing ENTER repeatedly
    if($global:lasdID -ne $item.id){
        
        $global:lasdID=$item.id
        
        # get the last command execution time (timespan)
        $diff = $item.EndExecutionTime-$item.StartExecutionTime
 
        # add it to the global $pshUse
        $global:pshUse = $global:pshUse.Add($diff)
 
        # string format total execution time in HH:mm:ss 
        # and last command execution time in mm:ss.milliseconds    
        $title = "{0:T} , Last [{1:mm:ss.fff}]" -f [datetime]($pshUse.ticks),[datetime]($diff.ticks)
 
        # update console title, show current directory, 
        # session execution time, and last command execution time
        $host.UI.RawUI.WindowTitle = "{0} | Session Execution Time: {1}" -f $pwd,$title
    }
    
    # write custom prompt with the last history command id 
    # each nested prompt level is indicated by an extra ">" 
    "PS {0}{1} " -f $item.id, (">"*($NestedPromptLevel+1))
}

 

Enjoy!

Sunday, September 16, 2007

Make a choice


Remember the old MS-DOS choice command? It prompts the user to make a choice in a batch program by displaying a prompt and pausing for the user to choose from a set of user-option keys.

In PowerShell, you can provide the same mechanism with .NET built-in classes without the need for external files. You can see this method in action on every cmdlet declared with -confirm parameter.

PS > Get-Process s* | Stop-Process -confirm

Confirm
Are you sure you want to perform this action?
Performing operation "Stop-Process" on Target "services (520)".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):



The user is presented with "plain old message box kind" prompt, built from a caption, message and number of choices to choose from:

Confirm = Caption
Are you sure you want to... = Message

And the choices:

[Y] Yes
[A] Yes to All
[N] No
[L] No to All
[S] Suspend

 

In my scripts I used to build this mechanism manually and write to the host the caption/message and choices until I 'found' this while digging on Mighty MoW's PowerTab scripts. The method is also described on MSDN PSHostUserInterface.PromptForChoice.

 

PS > $host.ui | gm

   TypeName: System.Management.Automation.Internal.Host.InternalHostUserInterface

Name                          MemberType
----                             ----------
...
Prompt                        Method
PromptForChoice         Method
PromptForCredential    Method
...

 

The definition for PromptForChoice is:


System.Int32 PromptForChoice(String caption, String message, Collection`1 choices, Int32 defaultChoice)

 

So, how can you implement it in your scripts?  Here is my variation:


$caption = "Confirm";
$message = "Are you sure?";
$yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","help";
$no = new-Object System.Management.Automation.Host.ChoiceDescription "&No","help";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no);
$answer = $host.ui.PromptForChoice($caption,$message,$choices,0)

switch ($answer){
    0 {"You entered 0"; break}
    1 {"You entered 1"; break}
}

write-host "Rest of script goes here..."

 

Here is the output:

PS C:\Scripts> .\PromptForChoice.ps1

Confirm
Are you sure?
[Y] Yes  [N] No  [?] Help (default is "Y"): n
You entered 1
Rest of script goes here...
PS >

 

Note that the special character "&" (ampersand) might be embedded in the label string to indicate that the next character is a "hot key" (for example, "Yes to &All"). The hot key allows the user to quickly set the input focus to this choice.

The default choice is determined by the order/index of the collection variables ($choices). To indicate no default, set defaultChoice to -1:

$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no);

$yes = 0
$no = 1


$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no,$retry);

$yes = 0
$no = 1
$retry = 2


 

There is also the "Help" option, which we didn't explicitly define.
The strings "help" in the $yes and $no variables are the help text to display when the user types "?"

$no = new-Object System.Management.Automation.Host.ChoiceDescription "&No","help";

 

I also wanted to implement the Suspend feature. Suspending the process is totally awesome. You have another chance to pause operation and get up to date, verified information, before proceeding. I knew that there is something called "Nested Prompt" and something inside felt that this is the place where it might be used. So I made some tests and here's the code with Suspend.


$caption = "Confirm";
$message = "Are you sure?";
$yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","help";
$no = new-Object System.Management.Automation.Host.ChoiceDescription "&No","help";
$Suspend = new-Object System.Management.Automation.Host.ChoiceDescription "&Suspend","help";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no,$suspend);
$Answer = $host.ui.PromptForChoice($caption,$message,$Choices,0)

switch ($Answer){

    0 {"You entered 0"; break}
    1 {"You entered 1"; break}

    2 {"You entered 2, entering nested prompt, type exit to return"; $Host.EnterNestedPrompt()}   
}

write-host "Rest of script goes here..."

 

Here is the output:

 

PS C:\Scripts> .\PromptForChoice.ps1

Confirm
Are you sure?
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"): s
You entered 2, entering nested prompt, type exit to return
PS >>> exit
Rest of script goes here...
PS >


Note that the PowerShell prompt is visually changing, colored in yellow ( adding >> to the default prompt) indicating that you're inside a nested prompt. To exit nested prompts simply type exit.