bash 中 emacs 风格的键绑定的令人困惑的行为

bash 中 emacs 风格的键绑定的令人困惑的行为

Bash 提供了许多有用的emacs-style keybindings简单命令行编辑功能。例如,Ctrl+w删除(“杀死”)光标左侧的单词。

另一个键绑定Alt+d应该是第一个键绑定的“镜像”。它应该从光标处删除一个单词。

然而,我注意到,这两个键绑定的行为并不完全对称。而Ctrl+w视为foo.bar一个词,Alt+d将其视为两个词

更烦人的# echo是,是两个词Ctrl+w,却是一个词Alt+d

这其中有什么逻辑吗?他们不以同样的方式对待文字有什么原因吗?

我有什么办法可以改变这个吗?

我正在使用 bashDebian Wheezy

答案1

不同的 bash 命令使用不同的单词概念。查看命令中各个命令的说明手动的

C-w杀死前一个空白。M-DEL(通常Alt+ ) 杀死到前一个单词边界,其中单词仅包含字母和数字(与和BackSpace相同),并类似地向前杀死。M-bM-fM-d

Bash 使用 Readline 库来处理用户输入,并且可以通过以下方式进行配置~/.inputrc或通过bind内置于~/.bashrc.您可以将一个密钥绑定到不同的密钥读行命令如果你希望。您还可以使用bind -x将键绑定到修改READLINE_LINE变量的 bash 函数。

例如,要使M-dKill 成为 shell 单词,请将其绑定到shell-kill-word在你的.bashrc

bind '"\M-d": shell-kill-word'

M-d删除以空格分隔的单词,没有内置函数,因此您需要编写宏或 shell 函数。由于没有由空格分隔的单词进行的运动命令,因此至少该部分需要一个函数。

delete_whitespace_word () {
  local suffix="${READLINE_LINE:$READLINE_POINT}"
  if [[ $suffix =~ ^[[:space:]]*[^[:space:]]+ ]]; then
    local -i s=READLINE_POINT+${#BASH_REMATCH[0]}
    READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}${READLINE_LINE:$s}"
  fi
}
bind -x '"\ed": delete_whitespace_word'

M-dkill成为一个以空格分隔的单词更复杂,因为据我所知,没有办法从bash代码访问kill环。因此,这需要一个函数来查找要杀死的部分的末尾,以及一个宏来跟随实际的杀死。

forward_whitespace_word () {
  local suffix="${READLINE_LINE:$READLINE_POINT}" 
  if [[ $suffix =~ ^[[:space:]]*[^[:space:]]+ ]]; then
    ((READLINE_POINT += ${#BASH_REMATCH[0]}))
  else
    READLINE_POINT=${#READLINE_LINE}
  fi
}
bind -x '"\C-xF": forward_whitespace_word'
bind '"\C-x\C-w": kill-region'
bind '"\ed": "\e \C-xF\C-x\C-w"'

所有这一切在 zsh 中都会容易得多。

答案2

Readline 已经有vi-fword和 ,vi-bword它使用空格作为单词边界,因此不需要 Gilles 的forward_whitespace_word函数。

vi-fword后跟unix-word-rubout( \C-w) 向后删除以空格分隔的单词(包括尾随空格)。

bind '"\eb":vi-bword'
bind '"\ef":vi-fword'
bind '"\ed":"\ef\C-w"'

vi-backward-word是 的别名vi-bwordvi-forward-word是 的别名vi-fwordvi-backward-bigword是 的别名vi-bWordvi-forward-bigword是 的别名vi-fWord

答案3

这是方法的结果阅读线对待“词”。Altd是以下的快捷方式kill-word

从当前单词的末尾处终止,或者如果在单词之间,则终止到下一个单词的末尾。字边界与 Mf 使用的相同(前向词)。

中的单词边界forward-word定义如下:

单词由字母和数字组成。

Ctrlw是 的快捷方式unix-word-rubout

杀掉点后面的字,使用空格作为单词边界

因此,要使用 的示例foo.bar,第一个命令将字符串视为由 分隔的两个“单词” .,而第二个命令看不到空格,因此将其视为单个“单词”。

相关内容