comportamento confuso de atalhos de estilo no estilo emacs no bash

4

O Bash oferece muitos emacs-style keybindings úteis para edição simples de linha de comando. Por exemplo, Ctrl + w apaga a palavra ("mata") do cursor.

Outro atalho, Alt + d deve ser um "espelho" do primeiro. Supõe-se que apague uma palavra diretamente do cursor.

No entanto, tenho notado, estas duas combinações de teclas não atuam de forma completamente simétrica. Considerando que Ctrl + w trata foo.bar como uma palavra, Alt + d a trata como duas palavras

Ainda mais irritante, # echo são duas palavras para Ctrl + w , mas uma palavra para Alt + d .

Existe alguma lógica nisso? Existe alguma razão pela qual eles não tratam as palavras da mesma maneira?

Existe alguma maneira de eu mudar isso?

Estou usando o bash em Debian Wheezy

    
por Martin Vegter 16.08.2014 / 23:41

3 respostas

4

Diferentes comandos bash usam diferentes noções de palavra. Verifique a descrição de cada comando no manual .

C-w mata para o espaço em branco anterior. M-DEL (geralmente Alt + BackSpace ) mata para o limite de palavras anterior, onde as palavras contêm apenas letras e dígitos (o mesmo que M-b e M-f ), e M-d mata de maneira semelhante.

O Bash usa a biblioteca Readline para processar a entrada do usuário e pode ser configurado via ~/.inputrc ou através do bind incorporado %código%. Você pode vincular uma chave a um comando readline diferente, se desejar. Você também pode usar ~/.bashrc para ligar uma chave a uma função bash que modifica a variável bind -x .

Por exemplo, para fazer READLINE_LINE matar uma palavra shell, vincule-a a M-d no seu shell-kill-word :

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

Para fazer com que .bashrc exclua uma palavra delimitada por espaço em branco, não há nenhuma função interna, portanto, é necessário escrever uma macro ou uma função de shell. Como não há comando de movimento que passe por palavras delimitadas por espaços em branco, você precisa de uma função pelo menos para essa parte.

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'

Para tornar M-d kill uma palavra delimitada por espaço em branco é mais complicada porque, até onde eu sei, não há como acessar o kill ring a partir do código bash. Então, isso requer uma função para encontrar o fim da porção a ser eliminada, e uma macro para seguir isso pela matança real.

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"'

Tudo isso seria muito mais fácil no zsh.

    
por 17.08.2014 / 02:09
1

Isso é resultado da maneira como readline trata de "palavras". Alt d é um atalho para kill-word :

Kill from point the end of the current word, or if between words, to the end of the next word. Word boundaries are the same as those used by M-f (forward-word).

Limites de palavras em forward-word são definidos assim:

Words are composed of letters and digits.

Considerando que Ctrl w é um atalho para unix-word-rubout :

Kill the word behind point, using white space as a word boundary.

Então, para usar o exemplo de foo.bar , o primeiro comando trata a string como duas "palavras" separadas por um . , enquanto a segunda não vê espaço em branco, então trata-a como uma única "palavra".

    
por 17.08.2014 / 00:04
1

Readline já tem vi-fword e vi-bword que usam espaço em branco como limites de palavras, portanto não há necessidade da função forward_whitespace_word de Gilles.

vi-fword seguido por unix-word-rubout ( \C-w ) exclui uma palavra delimitada por espaço em branco (incluindo espaços à direita).

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

vi-backward-word é um alias para vi-bword , vi-forward-word é um alias para vi-fword , vi-backward-bigword é um alias para vi-bWord e vi-forward-bigword é um alias para vi-fWord .

    
por 21.10.2014 / 02:01