Coming Soon: GET:IT Endpoint Management 1-Day Conference on September 28th at 9:30 AM ET Coming Soon: GET:IT Endpoint Management 1-Day Conference on September 28th at 9:30 AM 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

Say Goodbye to Traditional PC Lifecycle Management

Traditional IT tools, including Microsoft SCCM, Ghost Solution Suite, and KACE, often require considerable custom configurations by T3 technicians (an expensive and often elusive IT resource) to enable management of a hybrid onsite + remote workforce. In many cases, even with the best resources, organizations are finding that these on-premise tools simply cannot support remote endpoints consistently and reliably due to infrastructure limitations.

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

Live Webinar: Active Directory Security: What Needs Immediate Priority!Live on Tuesday, October 12th at 1 PM ET

Attacks on Active Directory are at an all-time high. Companies that are not taking heed are being punished, both monetarily and with loss of production.

In this webinar, you will learn:

  • How to prioritize vulnerability management
  • What attackers are leveraging to breach organizations
  • Where Active Directory security needs immediate attention
  • Overall strategy to secure your environment and keep it secured

Sponsored by: