Como obter a conclusão da tabulação ao usar chaves no Bash

2

Eu quero ter a conclusão da tabulação ao usar chaves em um comando no bash.

Por exemplo:

cp ~/html/{foo bar.txt whatever} /var/www/html/

Eu quero a conclusão da tabulação para os arquivos indicados nas chaves

    
por Weston Ganger 26.01.2015 / 22:46

3 respostas

0

Existem duas maneiras de interpretar a pergunta: você deseja a conclusão enquanto digita os nomes antes do fechamento } (executando a conclusão de arquivos em outro diretório); ou você deseja que a conclusão expanda e substitua os nomes (válidos) após o fechamento de } . A expansão { } expande todas as opções, ao contrário da globbing, que expande os nomes dos arquivos existentes , embora usando "," em vez de espaço.

Com o bash, a maneira esperada de usar {} para criar uma lista de palavras é:

cp ~/html/{foo,bar.txt,whatever}  ...

Existem duas opções: associar uma ligação de tecla readline a uma função shell ou conclusão programável.

A abordagem readline oferece uma mão livre para reescrever completamente o comando atual, mas o problema é que ele não separa a linha de comando atual, então você tem um problema de análise não-trivial.

A conclusão programável faz tokenizar a linha de comando, mas você só pode modificar / substituir a palavra atual, o que significa que você não pode (facilmente) preservar o prefixo path/{ durante a edição.

O único recurso bash nativo relacionado é o readline function shell-expand-line para o bash pretende fazer "todas as expansões de palavra do shell", isso é vinculado por padrão a \M-\C-e (Esc \C-e ). Pode-se razoavelmente esperar que esta expansão seja todos os sete tipos de expansão indicados na página man (bash-4.3):

Expansion is performed on the command line after it has been split into
words.   There are seven kinds of expansion performed: brace expansion,
tilde expansion, parameter and variable  expansion,  command  substitu‐
tion, arithmetic expansion, word splitting, and pathname expansion.

Você pode experimentar digitando (sem pressionar Voltar)

echo {1..2} ~root $0 $LANG 'echo foo' $((1+2) "a b" /etc/p[aeiou]*

E então Meta Ctrl E (ou ESC + Ctrl E se você estiver usando um teclado Meta-impaired).

Nem a primeira nem a última expansão (chave e caminho) funcionam para mim, então a documentação e a implementação não combinam com a IMHO.

O que se segue é uma solução alternativa, em vez de completa, usando a guia para expansão. Você precisa se certificar de usar a sintaxe correta para a expansão de chaves, especificamente com uma vírgula para separar os itens, não um espaço.

function _expand() {
   [[ -z "${READLINE_LINE}" ]] && return
   eval local aa=( ${READLINE_LINE} )       # not-quoted, eval needed
   [[ ${#aa} -eq 0 ]] && return             # parse problem
   printf -v READLINE_LINE "%s " "${aa[@]}"
   READLINE_POINT=${#READLINE_LINE}         # eol, predictable at least
}

bind -x '"\C-_":_expand'

Isto liga Ctrl _ a uma função bash que usa eval para preencher uma matriz, causando todas as expansões normais (como acima, e não incluindo histórico) para ocorrer e, em seguida, recria a linha de comando.

Comece a digitar, pressione Ctrl _ quando quiser algo expandido e Retornar como normal quando estiver pronto.

Isso é mais poderoso que a guia, mas menos preciso, pois expande a linha de entrada completa. Existem alguns casos em que isso não se expandirá como esperado, especificamente onde vários metacaracteres de shell confundem a lógica eval simples.

Em vez de projetar uma solução bash em excesso, a sugestão de zsh de Gille vale a pena.

    
por 27.01.2015 / 18:58
0

As chaves (que, por definição, são "cacheadas") referem-se a uma lista de fragmentos de texto, separados por vírgulas. Embora seja mais usado para nomes de arquivos, ele é expandido antes que as expansões de nome de arquivo sejam tratadas. Portanto, não há capacidade de expansão definitiva, porque bash não pode saber se você está realmente digitando um nome de arquivo ou não.

    
por 27.01.2015 / 18:54
0

De man bash :

COMP_WORDBREAKS
       The  set  of  characters  that  the readline
       library treats as word separators when  per‐
       forming word completion.  If COMP_WORDBREAKS
       is unset, it loses its  special  properties,
       even if it is subsequently reset.

Eu achei interessante, então eu fiz:

printf %q\n "${COMP_WORDBREAKS}"

... para ver seu valor padrão. Isso me pegou:

$' \t\n"\'@><=;|&(:'

Então eu imaginei, por que não ...?

$ COMP_WORDBREAKS="{}$COMP_WORDBREAKS"
$ {/<TAB><TAB>

... onde a parte <TAB> representa o fato de pressionar a tecla <TAB> , e vi:

$ {/
bin/      esp/      lib/      opt/      run/      sys/      usr/      
boot/     etc/      lib64/    proc/     sbin/     testuser/ var/      
dev/      home/     mnt/      root/     srv/      tmp/

Então, isso foi legal. Eu então fiz:

$ COMP_WORDBREAKS=${COMP_WORDBREAKS#??}
$ {/<TAB><TAB>

... e nada aconteceu.

    
por 28.01.2015 / 02:12