
En bash, puedes usar M- fy M- bpara mover el cursor una palabra hacia adelante y hacia atrás, pero ¿hay alguna manera de mover una?argumento¿hacia delante o hacia atrás? Si no está listo para usar, ¿quizás mediante alguna configuración?
En otras palabras, me gustaría mover el cursor para navegar entre las posiciones marcadas a continuación.
cp "foo bar.txt" "/tmp/directory with space"
^ ^ ^
| | |
Respuesta1
Sé que estás usando bash y no estoy seguro de que lo que pides sea posible en bash. Lo que les mostraré es cómo implementar la función solicitada en ZSH. (ZSH es un poco como un bash mejorado: si cambias, aún deberías seguir siendo competente).
En ZSH existe el editor de líneas ZSH (abreviado zle). Esto proporciona todas las teclas de movimiento como comandos vinculables, muy parecido a bash. Donde va más allá es en la capacidad de definir comandos personalizados. Un comando personalizado es cualquier función de shell que se haya convertido en un widget.
Estas funciones pueden ejecutar otros comandos y también obtienen acceso a varias variables que son de interés para su problema. De los que hablaré son:
- $BUFFER: esta es la línea completa que estás editando actualmente
- $CURSOR: esta es la posición de la inserción en la línea actual
También hay otros disponibles, como:
- $LBUFFER: esto es todo lo que está antes del cursor
- $RBUFFER: esto es todo después del cursor
Ahora sucede que ZSH no sólo es capaz de proporcionar combinaciones de teclas personalizadas, sino que también tiene un conjunto mucho más completo de operaciones que puede realizar con variables. Uno de los que resulta interesante para este problema es:
- z: divide el resultado de la expansión en palabras usando el análisis de shell para encontrar las palabras, es decir, teniendo en cuenta cualquier cita en el valor.
Puedes asignar el $BUFFER expandido directamente a una variable, así:
line=${(z)BUFFER}
(La línea ahora es una matriz, pero, curiosamente, esta matriz comienza en el índice 1, ¡a diferencia de bash!)
Esto no realizará ninguna expansión de caracteres globales, por lo que devolverá una matriz de los argumentos reales en su línea actual. Una vez que tenga esto, le interesará la posición del punto inicial de cada palabra en el búfer. Lamentablemente, es posible que tengas varios espacios entre dos palabras cualesquiera, así como palabras repetidas. Lo mejor que se me ocurre en este momento es eliminar cada palabra que se está considerando del búfer actual a medida que la considera. Algo como:
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
¡Ya casi llegamos! Lo que se necesita es una manera de ver qué palabra es la última palabra antes del cursor y qué palabra es el comienzo de la siguiente palabra después del cursor.
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
Hasta ahora he mostrado cómo se puede derivar el índice apropiado al que mover el cursor. Pero no te he mostrado cómo mover el cursor ni cómo vincular estas funciones a las teclas.
La variable $CURSOR se puede actualizar y, si lo hace, puede mover el punto de inserción actual. ¡Muy fácil!
Vincular funciones a teclas implica un paso intermedio: vincular primero un widget:
zle -N WIDGET_NAME FUNCTION_NAME
Luego puede vincular el widget a una clave. Probablemente tendrás que buscar los identificadores de clave específicos, pero normalmente solo me vinculo con Ctrl-LETTER, lo cual es bastante fácil:
bindkey '^LETTER' WIDGET_NAME
Juntemos todo esto y solucionemos su problema:
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
En lo que respecta a mis propias pruebas, esto parece funcionar. Probablemente querrás cambiar las claves a las que se vincula. Como referencia, lo probé con la línea:
one 'two three' "four five" "'six seven' eight nine" * **/ **/**/*
^ ^ ^ ^ ^ ^ ^ ^
y navegó entre los signos de intercalación. No se envuelve.
Respuesta2
Sí, en man bash dice que puedes usar
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.
Una secuencia de metacaracteres no entrecomillados es un argumento.
No dice que estén vinculados a ninguna clave de forma predeterminada y no lo tengo en mi inputrc, pero en mi shell están vinculados a CMf y CMb respectivamente. Si no, vincúlelos en su inputrc con
"\C-M-f": shell-forward-word
"\C-M-b": shell-backward-word
Respuesta3
Utilizo Ctrl+ sy Ctrl+ rpara buscar (y moverme) interactivamente hacia adelante y hacia atrás a través de la línea de comando (y a través del historial de comandos). Si bien no es exactamente lo que pide Andreas, es posible que pueda utilizar estas funciones de búsqueda interactiva para pasar rápidamente al argumento de su elección.
Si tu corresbind -P
bash, puedes ver todos los archivos de bash.acciones vinculables.
Consulte el siguiente enlace para obtener más información sobre otras opciones de movimiento del cursor:
https://stackoverflow.com/q/657130