When Windows PowerShell 3.0 shipped, the team created an extensibility mechanism to allow a third party to “take over” the line editing experience. That hook is a function called PSConsoleHostReadline that PowerShell will call – if it exists – to read a line of text from the user. The simplicity of this interface belies the potential complexity of what it needs to do. Users expect to be able to do more than just type a command, mistake free and press enter. No, they want editing features like cursor left/right, backspace, delete, kill to end of line, multiline editing, etc.
Fortunately for the community, one of the PowerShell team members – Jason Shirk – has implemented a very nice replacement line editor that uses this hook. It is called PSReadLine and you can get it here on GitHub. There are no releases on GitHub at this point in time but you can just download the PSReadline.zip file from the master branch, be sure to unblock the ZIP file and then extract the module your Modules folder. Alternatively, if you have PsGet you can install PSReadLine using its Install-Module command e.g:
C:\PS> Install-Module PSReadLine
After installation, import the PSReadLine module to have it take over your command line editing experience.
C:\PS> Import-Module PSReadLine
Note: the PSReadLine module only works for the PowerShell.exe console. It does not work for the PowerShell ISE console pane.
You have now imported PSReadLine, so what does it get you? If you use the default Windows (cmd) mode you get the standard CMD line editing features:
- Home/End navigation
- Up/DownArrow history navigation
- Right/Left Arrow single char navigation
- Ctrl+Right/LeftArrow word navigation
- Ctrl+Home to delete from cursor to beginning of line
- Ctrl+End to delete from cursor to end of line
- Esc to revert line
- Delete to delete a char
- Backspace to delete char before the cursor
You also get tab & shift+tab completion that you expect from the PowerShell console. So what else does it buy you? Well syntax color for one thing:
And how about indicating when you have a syntax error:
Notice the red “>” in my prompt? That is telling me I have a syntax error in this command. If you look at the end, I’m missing a closing “}”. Type in that closing “}” and the red goes away.
How about being able to paste into the console using Ctrl+V like you can in ISE. PSReadLine has got that covered. Ever deleted something accidentally in the line and wanted to get it back? Try the handy Ctrl+Z/Ctrl+Y shortcuts for Undo and Redo. Yes!! This line editor has a full blown undo/redo stack.
Ever had a command go over a single line? You know multiline commands are a pain in the PowerShell console because:
- You can’t go to a previous line to edit it. You have to completely start over. Pressing the UpArrow just cycles through history on the current line which means your current line edits get blown away.
- You can’t use Up/DownArrow history recall to recall the *whole* multiline command. You can only recall one line at a time which makes multiline commands a major PITA to recall & edit in the console.
Check this out with PSReadLine. It doesn’t “look” much different than standard PowerShell e.g.:
But you can use the Right/LeftArrow keys to move the cursor between different lines and then edit those lines. You still don’t want to use the Up/DownArrow keys for line navigation as these keys are still bound to history recall. However, when you do need to recall this multiline command using the UpArrow, what you get is exactly what you see above. All three lines included in that one history entry *AND* you can press LeftArrow to move up to the previous lines to edit them.
Last up is my favorite feature called PossibleCompletions. If I type “Get-Process –<Ctrl+Space>” this is what PSReadLine gives me:
Seriously! Is that cool or what!? PSReadLine shows you all the parameters in this case. If you had typed part of the parameter name, it would have shown all parameters that would have matched what you typed. By the way, this also works for parameter argument values e.g.:
It also works for static and instance type members e.g.:
There are more capabilities in PSReadLine but most of those come with Emacs mode. However you can create your own keybindings to do whatever command PSReadLine offers. If you want to use the Emacs mode, execute this command:
If you like this new command line editing experience as much as I do you will want this in your profile script. This is my section of profile script for PSReadLine:
if ($host.Name -eq 'ConsoleHost') { Import-Module PSReadline Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward Set-PSReadlineKeyHandler –Key DownArrow -Function HistorySearchForward }
Note that I’ve defined a few more keybindings that allow me to use the UpArrow and DownArrow keys to not only navigate backward and forward in history on an empty line but navigate based on a match as well. That is, when I type the beginning of some command line, the UpArrow and DownArrow keys search history backward or forward respectively for command lines that match what’s been typed.
If you spend a lot of time at the PowerShell console you owe it yourself to give this module a try. And if you run into issues, remember these are still somewhat early bits, the project developer has been responsive to fixing bugs. You can post/view issues here on GitHub. For more info on PSReadLine, check out the about_PSReadline topic after you have installed the module.
Update 12-29-2013:
Jason has delivered some new PSReadLine presents for the holidays. Version 1.0.0.6 supports some notable new features. My favorite of which is that you can select text using just the keyboard. As if you were in Notepad or VS, press Shift+Right/LeftArrow to select one character at a time or use Ctrl+Shift+Right/LeftArrow to select whole words at a time.
Once you have the desired text selected you can press Ctrl+x to cut or Ctrl+Shift+c to copy. Before careful with the copy keyboard shortcut because if you forgot to press Shift, the resulting Ctrl+c will abort the current command line and you can’t get it back – not even with Ctrl+z (undo). I wonder if perhaps the default copy shortcut should be something like Shift+Insert – old school – to avoid any potential mishaps with Ctrl+c?
You might find the following custom keyhandler handy for selecting the whole command line with Ctrl+A:
Set-PSReadlineKeyHandler -Chord Ctrl+A ` -BriefDescription SelectEntireCommandLine ` -Description "Selects the entire command line" ` -ScriptBlock { param($key, $arg) [PSConsoleUtilities.PSConsoleReadLine]::BeginningOfLine($key, $arg) $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) while ($cursor -lt $line.Length) { [PSConsoleUtilities.PSConsoleReadLine]::SelectForwardChar($key, $arg) [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) } }
Another feature that has been added is CharacterSearch and CharacterSearchBackward. By default these are mapped to F3 and Shift+F3. Say you are at the beginning of the command line with multiple pipeline stages. Press F3, then press Shift+\ to specify the “|” character. PSReadLine will advance the cursor to the next “|” character. This is cool. But the next two custom keyhandlers pretty nifty:
Set-PSReadlineKeyHandler -Chord Ctrl+\ ` -BriefDescription SearchForwardPipeChar ` -Description "Searches forward for the next pipeline character" ` -ScriptBlock { param($key, $arg) [PSConsoleUtilities.PSConsoleReadLine]::CharacterSearch($key, '|') } Set-PSReadlineKeyHandler -Chord Ctrl+Shift+\ ` -BriefDescription SearchBackwardPipeChar ` -Description "Searches backward for the next pipeline character" ` -ScriptBlock { param($key, $arg) [PSConsoleUtilities.PSConsoleReadLine]::CharacterSearchBackward($key, '|') }
These two keyhandlers will allow you to navigate around your command line by jumping forward and backward between pipeline stages. They use CharacterSearch and CharacterSearchBackward to move around the command line by searching for “|” characters. So Ctrl+\ moves forward to the next “|” character and Ctrl+Shift+\ moves backwards to the previous “|” character.
And finally, some help for quickly finding out what keyhandlers are mapped to which keyboard shortcuts, just press Ctrl+Alt+? which on a US keyboard is really Ctrl+Alt+Shift+/ and you get this information:
Key Function Description --- -------- ----------- Enter AcceptLine Accept the input or move to the... Shift+Enter AddLine Move the cursor to the next lin... Escape RevertLine Equivalent to undo all edits (c... LeftArrow BackwardChar Move the cursor back one charac... RightArrow ForwardChar Move the cursor forward one cha... Ctrl+LeftArrow BackwardWord Move the cursor to the beginnin... Ctrl+RightArrow NextWord Move the cursor forward to the ... Shift+LeftArrow SelectBackwardChar Adjust the current selection to... Shift+RightArrow SelectForwardChar Adjust the current selection to... Ctrl+Shift+LeftArrow SelectBackwardWord Adjust the current selection to... Ctrl+Shift+RightArrow SelectNextWord Adjust the current selection to... UpArrow PreviousHistory Replace the input with the prev... DownArrow NextHistory Replace the input with the next... Home BeginningOfLine Move the cursor to the beginnin... End EndOfLine Move the cursor to the end of t... Delete DeleteChar Delete the character under the ... Backspace BackwardDeleteChar Delete the charcter before the ... Ctrl+Spacebar PossibleCompletions Display the possible completion... Tab TabCompleteNext Complete the input using the ne... Shift+Tab TabCompletePrevious Complete the input using the pr... Ctrl+v Paste Paste text from the system clip... Ctrl+c CancelLine Abort editing the current line ... Ctrl+C Copy Copy selected region to the sys... Ctrl+l ClearScreen Clear the screen and redraw the... Ctrl+x Cut Delete selected region placing ... Ctrl+y Redo Redo an undo Ctrl+z Undo Undo a previous edit Ctrl+Backspace BackwardKillWord Move the text from the start of... Ctrl+Delete KillWord Move the text from the cursor t... Ctrl+End ForwardDeleteLine Delete text from the cursor to ... Ctrl+Home BackwardDeleteLine Delete text from the cursor to ... Ctrl+] GotoBrace Go to matching brace Ctrl+Alt+? ShowKeyBindings Show all key bindings Alt+? WhatIsKey Show the key binding for the ne... F3 CharacterSearch Read a character and move the c... Shift+F3 CharacterSearchBackward Read a character and move the c...
And to find out what a specify keyboard shortcut is mapped to, pres Alt+? (or on a US keyboard Alt+Shift+/) and then the keyboard shortcut. For example, if I press Alt+Shift+/ and then Ctrl+Shift+LeftArrow I get this information:
Ctrl+Shift+LeftArrow: SelectBackwardWord - Adjust the current selection to inclu de the previous word
Major kudos to Jason and this awesome module. It is funny how quickly you just get used to it and then when you’re on someone else’s PC without this module – well – you convince them pretty quickly that they need PSReadLine.
In closing, I’ll leave you with two more custom keyhandlers I have in my profile. The first creates a pair of single or double quotes and puts the cursor in the middle:
Set-PSReadlineKeyHandler -Chord "Ctrl+'","Ctrl+Shift+'" ` -BriefDescription SmartInsertQuote ` -Description "Insert paired quotes if not already on a quote" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [PSConsoleUtilities.PSConsoleReadline]::GetBufferState([ref]$line, [ref]$cursor) $keyChar = $key.KeyChar if ($key.Key -eq 'Oem7') { if ($key.Modifiers -eq 'Control') { $keyChar = "`'" } elseif ($key.Modifiers -eq 'Shift','Control') { $keyChar = '"' } } if ($line[$cursor] -eq $keyChar) { # Just move the cursor [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor + 1) } else { # Insert matching quotes, move cursor to be in between the quotes [PSConsoleUtilities.PSConsoleReadLine]::Insert("$keyChar" * 2) [PSConsoleUtilities.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) [PSConsoleUtilities.PSConsoleReadLine]::SetCursorPosition($cursor - 1) } }
The second handler was inspired by mjolinor’s Clip-ToArray ISE add-on. It is a more basic version of his function which I call – PasteAsHereString. It is indeed handy and could be easily modified to do all that the original Clip-ToArray function does.
Set-PSReadlineKeyHandler -Chord Ctrl+Shift+v ` -BriefDescription PasteAsHereString ` -Description "Pastes the clipboard text as a here string" ` -ScriptBlock { param($key, $arg) Add-Type -AssemblyName System.Windows.Forms $str = [windows.forms.clipboard]::GetText() -replace '(?m)[`n\s*]+$','' [PSConsoleUtilities.PSConsoleReadline]::Insert("@'`n${str}`n'@") }
![](http://pixel.wp.com/b.gif?host=rkeithhill.wordpress.com&blog=18780344&post=302&subd=rkeithhill&ref=&feed=1)