在 bash 中,你可以使用M-f和M-b将光标向前和向后移动一个单词,但是有没有办法移动一个争论向前还是向后?如果不是开箱即用,也许可以通过某种配置来实现?
换句话说,我希望光标在下面标记的位置之间导航。
cp "foo bar.txt" "/tmp/directory with space"
^ ^ ^
| | |
答案1
我知道你正在使用 bash,但我不确定你问的问题在 bash 中是否可以实现。我将向你展示如何在 ZSH 中实现所请求的功能。(ZSH 有点像改进的 bash - 如果你切换,那么你仍然应该保持熟练)。
ZSH 中有 ZSH 行编辑器(简称 zle)。它将所有移动键作为可绑定命令提供,与 bash 非常相似。它更进一步的是能够定义自定义命令。自定义命令是任何已转换为小部件的 shell 函数。
这些函数可以执行其他命令,还可以访问一些与你的问题相关的变量。我将讨论的是:
- $BUFFER - 这是您当前正在编辑的整行
- $CURSOR - 这是当前行中插入的位置
还有其他可用的,例如:
- $LBUFFER - 这是光标前的所有内容
- $RBUFFER - 这是光标后的所有内容
现在 ZSH 不仅能够提供自定义键绑定,它还拥有一套更全面的可对变量执行的操作。对于这个问题来说,其中一个有趣的操作是:
- z - 使用 shell 解析将扩展结果拆分为单词以查找单词,即考虑值中的任何引用。
您可以将扩展的 $BUFFER 直接分配给变量,如下所示:
line=${(z)BUFFER}
(line 现在是一个数组,但令人讨厌的是,与 bash 不同,这个数组从索引 1 开始!)
这不会执行任何通配符的扩展,因此它将返回当前行中实际参数的数组。一旦有了这个,您就会对缓冲区中每个单词的起始点的位置感兴趣。不幸的是,任何两个单词之间都有多个空格,以及重复的单词。此时我能想到的最好的办法是,在考虑每个单词时,将其从当前缓冲区中删除。例如:
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
我们现在就快到了!我们需要一种方法来查看哪个单词是光标前的最后一个单词,哪个单词是光标后的下一个单词的开头。
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
到目前为止,我已经向您展示了如何获取光标要移动到的适当索引。但我还没有向您展示如何移动光标,或者如何将这些功能绑定到键。
$CURSOR 变量可以更新,如果你这样做,你就可以移动当前插入点。非常简单!
将功能绑定到键涉及首先绑定到小部件的中间步骤:
zle -N WIDGET_NAME FUNCTION_NAME
然后,您可以将小部件绑定到某个键。您可能需要查找特定的键标识符,但我通常只绑定到 Ctrl-LETTER,这很容易:
bindkey '^LETTER' WIDGET_NAME
让我们把所有这些放在一起并解决您的问题:
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
就我自己的测试而言,这似乎可以完成工作。您可能想要更改它绑定的键。作为参考,我使用以下行进行了测试:
one 'two three' "four five" "'six seven' eight nine" * **/ **/**/*
^ ^ ^ ^ ^ ^ ^ ^
并且它在插入符号之间导航。它不会换行。
答案2
是的,在 man bash 中它说你可以使用
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.
一系列未引用的元字符是一个参数。
它没有说它们默认绑定到任何键,而且我的 inputrc 中也没有,但在我的 shell 中它们分别绑定到 CMf 和 CMb。如果没有,请在您的 inputrc 中绑定它们
"\C-M-f": shell-forward-word
"\C-M-b": shell-backward-word
答案3
我使用Ctrl+s和Ctrl+r在命令行(以及命令历史记录)中以交互方式向前和向后搜索(和移动)。虽然这不是 Andreas 所要求的,但您可以使用这些交互式搜索功能快速移动到您选择的参数。
如果你bind -P
在 bash 中运行,你可以看到 bash 的可绑定动作。
有关其他光标移动选项的更多讨论请参阅以下链接:
https://stackoverflow.com/q/657130