Möglichkeit, den Cursor in Bash durch Argumente zu bewegen?

Möglichkeit, den Cursor in Bash durch Argumente zu bewegen?

In Bash können Sie M- fund M- verwenden b, um den Cursor ein Wort vorwärts und rückwärts zu bewegen. Gibt es jedoch eine Möglichkeit, ein Wort vorwärts oder rückwärts zu bewegen?Streitvorwärts oder rückwärts? Wenn nicht sofort, dann vielleicht durch eine Konfiguration?

Mit anderen Worten, ich möchte mit dem Cursor zwischen den unten markierten Positionen navigieren.

cp "foo bar.txt" "/tmp/directory with space"
^  ^             ^
|  |             |

Antwort1

Ich weiß, dass Sie Bash verwenden, und ich bin nicht sicher, ob das, was Sie verlangen, in Bash möglich ist. Ich werde Ihnen zeigen, wie Sie die gewünschte Funktion in ZSH implementieren. (ZSH ist ein bisschen wie ein verbessertes Bash – wenn Sie wechseln, sollten Sie trotzdem kompetent bleiben).

In ZSH gibt es den ZSH Line Editor (kurz zle). Dieser stellt alle Bewegungstasten als bindbare Befehle bereit, ähnlich wie Bash. Darüber hinaus können benutzerdefinierte Befehle definiert werden. Ein benutzerdefinierter Befehl ist jede Shell-Funktion, die in ein Widget umgewandelt wurde.

Diese Funktionen können andere Befehle ausführen und erhalten außerdem Zugriff auf mehrere Variablen, die für Ihr Problem von Interesse sind. Ich werde über diese sprechen:

  • $BUFFER – dies ist die gesamte Zeile, die Sie gerade bearbeiten
  • $CURSOR - dies ist die Position des Inserts in der aktuellen Zeile

Es sind auch andere verfügbar, wie:

  • $LBUFFER - das ist alles vor dem Cursor
  • $RBUFFER - das ist alles nach dem Cursor

Nun ist es so, dass ZSH nicht nur benutzerdefinierte Tastenkombinationen bereitstellen kann, sondern auch über einen weitaus umfassenderen Satz von Operationen verfügt, die Sie an Variablen ausführen können. Eine der für dieses Problem interessanten Operationen ist:

  • z - Teilen Sie das Ergebnis der Erweiterung in Wörter auf. Verwenden Sie dazu die Shell-Analyse, um die Wörter zu finden, d. h. berücksichtigen Sie dabei alle Anführungszeichen im Wert.

Sie können den erweiterten $BUFFER direkt einer Variablen zuweisen, und zwar wie folgt:

line=${(z)BUFFER}

(Zeile ist jetzt ein Array, aber ärgerlicherweise beginnt dieses Array im Gegensatz zu Bash beim Index 1!)

Dadurch wird keine Erweiterung der Globbing-Zeichen durchgeführt, sondern ein Array mit den tatsächlichen Argumenten in Ihrer aktuellen Zeile zurückgegeben. Sobald Sie dies haben, sind Sie an der Position des Startpunkts jedes Worts im Puffer interessiert. Leider kann es durchaus vorkommen, dass Sie mehrere Leerzeichen zwischen zwei beliebigen Wörtern sowie wiederholte Wörter haben. Das Beste, was mir an dieser Stelle einfällt, ist, jedes Wort, das berücksichtigt wird, aus dem aktuellen Puffer zu entfernen, während Sie es berücksichtigen. So etwas wie:

buffer=$BUFFER
words=${(z)buffer}
for word in $words[@]
do
    # doing regular expression matching here,
    # so need to quote every special char in $word.
    escaped_word=${(q)word}
    # Fancy ZSH to the rescue! (q) will quote the special characters in a string.

    # Pattern matching like this creates $MBEGIN $MEND and $MATCH, when successful
    if [[ ! ${buffer} =~ ${${(q)word}:gs#\\\'#\'#} ]]
    then
        echo "Something strange happened... no match for current word"
        return 1
    fi
    buffer=${buffer[$MEND,-1]}
done

Jetzt sind wir fast am Ziel! Was wir brauchen, ist eine Möglichkeit, zu erkennen, welches Wort das letzte Wort vor dem Cursor ist und welches Wort der Anfang des nächsten Wortes nach dem Cursor ist.

buffer=$BUFFER
words=${(z)buffer}
index=1
for word in $words[@]
do
    if [[ ! ${buffer} =~ ${${(q)word}:gs#\\\'#\'#} ]]
    then
        echo "Something strange happened... no match for current word"
        return 1
    fi

    old_length=${#buffer}
    buffer=${buffer[$MEND,-1]}
    new_length=${#buffer}
    old_index=$index
    index=$(($index + $old_length - $new_length))

    if [[ $old_index -lt $CURSOR && $index -ge $CURSOR ]]
    then
        # $old_index is the start of the last argument.
        # you could move back to it.
    elif [[ $old_index -le $CURSOR && $index -gt $CURSOR ]]
    then
        # $index is the start of the next argument.
        # you could move forward to it.
    fi
    # Obviously both of the above conditions could be true, you would
    # have to have a way to tell which one you wanted to test - but since
    # you will have two widgets (one forward, one back) you can tell quite easily. 
done

Bisher habe ich gezeigt, wie Sie den geeigneten Index ermitteln können, zu dem der Cursor bewegt werden soll. Aber ich habe Ihnen nicht gezeigt, wie Sie den Cursor bewegen oder wie Sie diese Funktionen an Tasten binden.

Die Variable $CURSOR kann aktualisiert werden. Wenn Sie dies tun, können Sie die aktuelle Einfügemarke verschieben. Ganz einfach!

Das Binden von Funktionen an Tasten erfordert zunächst den Zwischenschritt der Bindung an ein Widget:

zle -N WIDGET_NAME FUNCTION_NAME

Sie können das Widget dann an eine Taste binden. Sie müssen wahrscheinlich die spezifischen Tastenkennungen nachschlagen, aber ich binde es normalerweise einfach an Strg-Buchstabe, was recht einfach ist:

bindkey '^LETTER' WIDGET_NAME

Lassen Sie uns all dies zusammenfassen und Ihr Problem beheben:

function move_word {
    local direction=$1

    buffer=$BUFFER
    words=${(z)buffer}
    index=1
    old_index=0
    for word in $words[@]
    do
        if [[ ! ${buffer} =~ ${${(q)word}:gs#\\\'#\'#} ]]
        then
            echo "Something strange happened... no match for current word $word in $buffer"
            return 1
        fi

        old_length=${#buffer}
        buffer=${buffer[$MEND,-1]}
        new_length=${#buffer}
        index=$(($index + $old_length - $new_length))

        case "$direction" in
            forward)
                if [[ $old_index -le $CURSOR && $index -gt $CURSOR ]]
                then
                    CURSOR=$index
                    return
                fi
                ;;
            backward)
                if [[ $old_index -lt $CURSOR && $index -ge $CURSOR ]]
                then
                    CURSOR=$old_index
                    return
                fi
                ;;
        esac
        old_index=$index
    done
    case "$direction" in
        forward)
            CURSOR=${#BUFFER}
            ;;
        backward)
            CURSOR=0
            ;;
    esac
}

function move_forward_word {
    move_word "forward"
}

function move_backward_word {
    move_word "backward"
}

zle -N my_move_backwards move_backward_word
zle -N my_move_forwards move_forward_word
bindkey '^w' my_move_backwards
bindkey '^e' my_move_forwards

Soweit ich es getestet habe, scheint das zu funktionieren. Sie werden wahrscheinlich die Tasten ändern wollen, an die es gebunden ist. Als Referenz habe ich es mit der Zeile getestet:

one 'two three' "four five"    "'six seven' eight nine" * **/ **/**/*
^  ^           ^           ^                           ^ ^   ^       ^

und es navigierte zwischen den Cursorn. Es wird kein Zeilenumbruch durchgeführt.

Antwort2

Ja, in man bash steht, dass Sie verwenden können

shell-forward-word
              Move forward to the end of the next word.  Words  are  delimited  
              by non-quoted shell metacharacters.

shell-backward-word  
              Move  back  to the start of the current or previous word.  Words  
              are delimited by non-quoted shell metacharacters.  

Eine Folge nicht in Anführungszeichen gesetzter Metazeichen ist ein Argument.

Es steht nicht, dass sie standardmäßig an eine Taste gebunden sind, und ich habe es auch nicht in meiner Eingaberc, aber in meiner Shell sind sie an CMf bzw. CMb gebunden. Wenn nicht, binden Sie sie in Ihrer Eingaberc mit

"\C-M-f": shell-forward-word

"\C-M-b": shell-backward-word  

Antwort3

Ich verwende Ctrl+ sund Ctrl+, rum interaktiv vorwärts und rückwärts durch die Befehlszeile (und durch den Befehlsverlauf) zu suchen (und zu navigieren). Auch wenn das nicht genau das ist, wonach Andreas sucht, können Sie diese interaktiven Suchfunktionen möglicherweise verwenden, um schnell zum Argument Ihrer Wahl zu gelangen.

Wenn Sie bind -Pin Bash ausführen, können Sie alle Bash-bindbare Aktionen.

Weitere Informationen zu anderen Optionen zur Cursorbewegung finden Sie unter dem folgenden Link:
https://stackoverflow.com/q/657130

verwandte Informationen