Learn What IT Pros Need to Know About Windows 11 - August 26th at 1 PM ET! Learn What IT Pros Need to Know About Windows 11 - August 26th at 1 PM ET!
Uncategorized

More Efficient PowerShell with PSReadline -- Part 6

We have been spending a ton of time on the great stuff that is in the PSReadline module, which is now a default part of PowerShell. If you are just jumping in, take some time to get caught up on past articles or you will be a little lost.

 

 

Sponsored Content

Read the Best Personal and Business Tech without Ads

Staying updated on what is happening in the technology sector is important to your career and your personal life but ads can make reading news, distracting. With Thurrott Premium, you can enjoy the best coverage in tech without the annoying ads.

Even though the module has been around for awhile, I never paid it much attention. Now that I have dug into it, I can see some terrific value. I think it will make my time at a PowerShell prompt more efficient and enjoyable. I spend most of my day with a PowerShell session running and other than a web browser and email, it is where I am most likely working. I thought I would share some of the things I am using with PSReadline. Some of these are drawn from the sample profile script I mentioned last time. But I have made a few modifications.

Graphical Command History

We already know that PSReadline maintains a historical record outside of PowerShell. You may also recall that in previous versions of PowerShell (and Windows) you used to be able to press F7 to get a graphical popup of recent commands. This was actually the command buffer from the CMD window that was running PowerShell. Regardless, I have a key handler assigned to F7 that uses Out-Gridview to display command history.

Set-PSReadlineKeyHandler -Key F7 -BriefDescription HistoryList -Description "Show command history with Out-Gridview. [$($env:username)]" -ScriptBlock {
    $pattern = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$pattern, [ref]$null)
    if ($pattern)
    {
        $pattern = [regex]::Escape($pattern)
    }

    $history = [System.Collections.ArrayList]@(
        $last = ''
        $lines = ''
        foreach ($line in [System.IO.File]::ReadLines((Get-PSReadlineOption).HistorySavePath))
        {
            if ($line.EndsWith('`'))
            {
                $line = $line.Substring(0, $line.Length - 1)
                $lines = if ($lines)
                {
                    "$lines`n$line"
                }
                else
                {
                    $line
                }
                continue
            }

            if ($lines)
            {
                $line = "$lines`n$line"
                $lines = ''
            }

            if (($line -cne $last) -and (!$pattern -or ($line -match $pattern)))
            {
                $last = $line
                $line
            }
        }
    )
    $history.Reverse()

    $command = $history | Select-Object -unique | Out-GridView -Title "PSReadline History - Select a command to insert at the prompt" -OutputMode Single
    if ($command)
    {
        [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert(($command -join "`n"))
    }
}

My version filters out duplicates. When I press F7 I get something like this:

My F7 Handler (Image Credit: Jeff Hicks)
My F7 Handler (Image Credit: Jeff Hicks)

If I select a command and press OK, the command is inserted at my prompt where I can modify and run it. Or if I type a string like ‘process’ and then press F7, the handler script block will filter commands that match the pattern.

Filtered history by pattern (Image Credit: Jeff Hicks)
Filtered History by Pattern (Image Credit: Jeff Hicks)

Now, I have an easy way to find a previous command.

 

Jump Lists

The other set of handlers I am getting a ton of use from are those that provide “jump list” functionality. With a jump list, I can assign a keyboard marker or shortcut to a directory. I can then invoke the shortcut to rapidly change to that folder. For example, with my PSReadline tools, I can change to a folder like C:\scripts. I then press Ctrl+Alt+J and press the keyboard shortcut I want to assign such as ‘s’. It can be any single keyboard character. I can repeat the process for any folder I am likely to use throughout the day. The data is stored in a hashtable. I also wrote a handler for Alt+J that displays my current assignments in a popup.

My Jump List (Image Credit: Jeff Hicks)
My Jump List (Image Credit: Jeff Hicks)

 

To jump to any directory, I press Ctrl+J and the shortcut key. With this, I can very quickly jump between frequently used directories. Because I want to always have these handlers, I use this code in my PowerShell profile script.

# Ctrl+Alt+j then type a key to mark the current directory.
# Alt+J to show the current shortcuts in a popup
# Ctrj+j then the same key will change back to that directory without
# needing to type cd and won't change the command line.

#pre-populate a global variable
$global:PSReadlineMarks = @{
[char]"s"="c:\scripts"
[char]"d"="c:\users\jeff\documents"
}

Set-PSReadlineKeyHandler -Key Ctrl+Alt+j -BriefDescription MarkDirectory -LongDescription "Mark the current directory. [$($env:username)]" -ScriptBlock {

#press a single character to mark the current directory
    $key = [Console]::ReadKey($true)
    if ($key.keychar -match "\w") {
        $global:PSReadlineMarks[$key.KeyChar] = $pwd
    }
    else {
        [Microsoft.PowerShell.PSConsoleReadLine]::Ding()
        Write-Warning "You entered an invalid character."
        [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
    }
}

Set-PSReadlineKeyHandler -Key Ctrl+j -BriefDescription JumpDirectory -LongDescription "Goto the marked directory.[$($env:username)]" -ScriptBlock {

    $key = [Console]::ReadKey()
    $dir = $global:PSReadlineMarks[$key.KeyChar]
    if ($dir)
    {
        cd $dir
        [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
    }
}

Set-PSReadlineKeyHandler -Key Alt+j -BriefDescription ShowDirectoryMarks -LongDescription "Show the currently marked directories in a popup. [$($env:username)]" -ScriptBlock {

    $data = $global:PSReadlineMarks.GetEnumerator() | Where {$_.key} | Sort key 
    $data | foreach -begin {
     [email protected]"
Key`tDirectory
---`t---------

"@
     } -process { 
     
     $text+="{0}`t{1}`n" -f $_.key,$_.value
     } 
     $ws = New-Object -ComObject Wscript.Shell
     $ws.popup($text,10,"Use Ctrl+J to jump") | Out-Null
     
     [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
   
}

I am pre-populating the hashtable with some values I know I always want but you don’t have to. The bottom line, at least for me, is that PSReadline helps me work much smarter and more efficiently. Anything that saves a few keystrokes here and there or eliminates an error-prone activity like typing (!) is worth my time to learn and leverage.

I hope you found this series interesting. If you are doing anything fun and interesting with PSReadline I would love to hear about it.

Related Topics:

BECOME A PETRI MEMBER:

Don't have a login but want to join the conversation? Sign up for a Petri Account

Register
Comments (0)

Leave a Reply

Register for Advanced Microsoft 365 Day!

GET-IT: Advanced Microsoft 365 1-Day Virtual Conference - Live August 24th!

Join us on Tuesday, August 24th and hear from Microsoft MVPs and industry experts about how to take advantage of Microsoft 365 at a technical level and dive deep into the features and functionality that will make your environment more secure and compliant.

RSVP Now

Sponsored By