Can I use regular expressions to search PowerShell command history via CTRL+R back-search?

Viewed 193

I have run a series of PowerShell commands, and I'd like to search through the history, using the CTRL + R back search feature using regular expressions. Either it's not implemented right now, or I'm doing something wrong.

Imagine that you have a command in your history: ssh username@host.name

I would like to press CTRL+R and type ssh.*host to return this command onto my PowerShell prompt. Right now, this doesn't seem to work.

Question: Can I use regular expressions to search PowerShell command history via CTRL+R back-search?

Expected Result

The back search returns a result based on a regular expression.

back-i-search: <expression>

Actual Result

The back search fails.

failed-bck-i-search: <expression>
1 Answers

Yyyyeach, you can. The shell does not give you much aid on it, but nothing stops you from developing your own script that would emulate this behavior. PSReadLine can come handy here. Check this out:

Set-PSReadlineKeyHandler -Chord Ctrl+r -ScriptBlock {
    $currentInput = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref] $currentInput, [ref] $null)

    $entries = [Microsoft.PowerShell.PSConsoleReadLine]::GetHistoryItems() | Where-Object CommandLine -match $currentInput | Select-Object CommandLine | ForEach-Object {$_.CommandLine.ToString()}
    $pointer = $entries.Count - 1

    while($pointer -ge 0 -and $pointer -lt $entries.Count) {
         [Microsoft.PowerShell.PSConsoleReadLine]::BeginningOfLine()
         [Microsoft.PowerShell.PSConsoleReadLine]::KillLine()
         [Microsoft.PowerShell.PSConsoleReadLine]::Replace(0, 0, $entries[$pointer])

        $userInp = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown").VirtualKeyCode

        if($userInp -eq 38) {
            $pointer -= 1;
        } elseif($userInp -eq 40) {
            $pointer += 1;
        } elseif($userInp -eq 13) {
            [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
            break;
        } else {
            break;
        }
    }
}

Usage

Type your expression

PS> ssh.*host

Press ctrl+r. The input should be replaced with the last entry matching the regex.

PS> ssh cobolcourse@mainserver.host

Now you can use up and down arrows to navigate the history. Press enter to accept or anything else to cancel.

How it works

The script just reads all of the PSReadLine history, filters it with what you need and feeds it to an array $entries. Then it scans for keys pressed to catch arrows to navigate through it and replaces the current prompt with the last visited entry.

It's very simple and most likely prone to edge-cases, but I think you can tinker on it further on. If you want to improve the performance, you can try to utilize pipelines, but then making it interactive will become more challenging. Anyways, good luck!

Related