búsqueda de historial inverso con expresiones regulares

10

Estoy buscando una herramienta que permita la búsqueda incremental inversa con soporte simple de expresión regular (o solo coincidencias múltiples). Por ejemplo, si quiero encontrar el comando 'foo bar baz', podría hacer algo como lo siguiente para encontrar el comando rápidamente:

CRTL-R (comienza la búsqueda) escriba 'foo' (coincide con el comando más reciente usando foo) continúe escribiendo 'foo | baz' (coincide con el comando más reciente que contiene 'foo' Y 'baz'.

Existe algo como esto? Si no, ¿cómo podría implementarlo yo mismo?

dibujante
fuente

Respuestas:

4

Widget personalizado history-incremental-multi-searchparazsh

Preparar

Cree un directorio e inclúyalo en su $fpathPor ejemplo, creé un directorio ~/.zsh/functionsy la línea fpath=($HOME/.zsh/functions $fpath)en mi .zshrc.

Ponga lo siguiente en un archivo nombrado history-incremental-multi-searchen ese directorio.

emulate -L zsh
setopt extended_glob

local oldbuffer=$BUFFER
local -i oldcursor=$CURSOR

local dir                # search direction
local chars              # input buffer
local -a words           # search terms
local -a found           # all history items that match first term
local -i hindex=$HISTNO  # current 
local -i lmatch          # last matched history item (for prev/next)

if [[ $WIDGET == *forward* ]]; then
    dir=fwd
else
    dir=bck
fi

function find-next {
    # split the input buffer on spaces to get search terms
    words=(${(s: :)chars})

    # if we have at least one search term
    if (( $#words )); then
        # get all keys of history items that match the first
        found=(${(k)history[(R)*$words[1]*]})
        if (( $#found )); then
            # search in widget direction by default
            # but accept exception in $1 for "prev match"
            search-${1:-$dir}
        else
            # no matches
            lmatch=$HISTNO
        fi
    else
        # no search terms
        lmatch=$HISTNO
        BUFFER=$oldbuffer
        CURSOR=$oldcursor
    fi
}

function search-fwd {
    # search forward through matches
    local -i i
    for (( i = $#found; i > 0; i-- )); do
        # but not before hindex as we're searching forward
        if [[ $found[$i] -gt $hindex ]]; then
            set-match $found[$i]
        fi
    done
}

function search-bck {
    # search backward through matches
    local -i i
    for (( i = 1; i <= $#found; i++ )); do
        # but not beyond hindex as we're searching backward
        if [[ $found[$i] -lt $hindex ]]; then
            set-match $found[$i]
        fi
    done
}

function set-match {
    # match history item against all terms and select it if successful
    local match=1
    local -i i
    for (( i = 2; i <= $#words; i++ )); do
        if [[ $history[$1] != *$words[$i]* ]]; then
            match=0
            break
        fi
    done
    if [[ $match -ne 0 ]]; then
        lmatch=$1
        BUFFER=$history[$1]
        CURSOR=$#BUFFER
        break
    fi
}

# display sub prompt
zle -R "${dir}-i-search-multi:"

# handle input keys
while read -k; do
    case $REPLY in
        # next
        $'\C-n' )
            hindex=$lmatch
            find-next
            ;;
        # prev
        $'\C-p' )
            hindex=$lmatch
            if [[ $dir == fwd ]]; then
                find-next bck
            else
                find-next fwd
            fi
            ;;
        # break
        $'\e' | $'\C-g' )
            BUFFER=$oldbuffer
            CURSOR=$oldcursor
            break
            ;;
        # accept
        $'\C-m' | $'\C-j' )
            if [[ $lmatch -eq $HISTNO ]]; then
                BUFFER=$oldbuffer
                CURSOR=$oldcursor
            else
                HISTNO=$lmatch
            fi
            break
            ;;
        # erase char
        $'\C-h' | $'\C-?' )
            chars=$chars[1,-2]
            hindex=$HISTNO
            find-next
            ;;
        # erase word
        $'\C-w' )
            if [[ $chars =~ \  ]]; then
                chars=${chars% *}
            else
                chars=
            fi
            hindex=$HISTNO
            find-next
            ;;
        # kill line
        $'\C-u' )
            chars=
            hindex=$HISTNO
            find-next
            ;;
        # add unhandled chars to buffer
        * )
            chars=${chars}${REPLY}
            hindex=$HISTNO
            find-next
            ;;
    esac

    zle -R "${dir}-i-search-multi: $words"
done

Ponga esto o búsquelo en su .zshrc:

autoload -U history-incremental-multi-search

# make new widgets from function
zle -N history-incremental-multi-search-backward history-incremental-multi-search
zle -N history-incremental-multi-search-forward history-incremental-multi-search

# bind the widgets to keys
bindkey '^Xr' history-incremental-multi-search-backward
bindkey '^Xs' history-incremental-multi-search-forward

Utilizar

Ahora debería ser capaz de iniciar una búsqueda incremental hacia atrás con Ctrl+X, r, adelante con Ctrl+X, s.

Escriba sus términos de búsqueda separados por espacio. Las siguientes teclas están disponibles para controlarlo:

  • ← Backspace: borrar personaje

  • Ctrl+W: borrar palabra

  • Ctrl+U: línea de matar

  • Ctrl+N: próximo partido

  • Ctrl+P: partido anterior

  • Ctrl+G/ Esc: cancelar búsqueda

  • Enter: aceptar

Esta solución probablemente se puede simplificar bastante. Es más una prueba de concepto funcional, con mucho margen de mejora.

peth
fuente
Gracias por tomarse el tiempo para escribir estas respuestas, esto me ayuda inmensamente. Espero que más personas tropiecen con esto y lo encuentren útil.
drewrobb
6

Puedes recorrer tu historial:

history | egrep '(foo|baz)'

Espero que eso ayude.

binfalse
fuente
0

A partir de la respuesta de @ peth:

Ahora viene con Zsh history-incremental-pattern-search-backward, no necesita definirlo usted mismo. Solo agregue la encuadernación de teclas. Prefiero anular ^Ragregando la siguiente línea a .zshrc:

bindkey '^R' history-incremental-pattern-search-backward

Ahora puede utilizar operadores glob (sic! Not rexex) en su búsqueda.

Sergey Romanovsky
fuente