Como personalizar a conclusão do comando Bash?

24

No bash, é fácil o suficiente configurar a conclusão personalizada dos argumentos de comando usando o completebuilt-in. Por exemplo, se, para um comando hipotético com uma sinopse de foo --a | --b | --c, você pudesse fazercomplete -W '--a --b --c' foo

Você também pode personalizar a conclusão que obtém quando pressiona Tabem um prompt vazio usando complete -E, por exemplo complete -E -W 'foo bar',. Pressionar tab no prompt vazio sugere apenas fooe bar.

Como personalizo a conclusão do comando em um prompt não vazio? Por exemplo, se eu estiver sentado em:

anthony@Zia:~$ f

como personalizar a conclusão de modo que a guia pressionada sempre seja concluída foo?

(O caso real que eu gostaria é de locTABlocalc. E meu irmão, que me levou a perguntar isso, quer isso com mplayer).

derobert
fonte
2
Você já pensou em simplesmente aliasing locpara localc? Sugiro alternativas porque, depois de algum tempo pesquisando e pesquisando, não encontrei uma maneira de personalizar a conclusão do bash dessa maneira. Pode não ser possível.
jw013
@ jw013 Sim, procurei por um tempo e também não consegui encontrar nada. Estou meio que esperando que alguém sugira mudar para o zsh também. Pelo menos se o zsh fizer isso, posso descansar confortavelmente sabendo que a próxima versão do bash também. 😀
derobert
você precisa pressionar TAB duas vezes e ele concluirá os comandos.
Floyd
11
Isso não vai quebrar todas as outras conclusões? Quero dizer, mesmo que seja possível, você já não seria capaz de conseguir locate, locale, lockfileou qualquer uma das outras expansões de loc. Talvez uma abordagem melhor seria mapear uma chave diferente para essa conclusão específica.
terdon
11
você pode dar um exemplo desse contexto (que levaria ao efeito desejado loc<TAB>->localc)?
Damienfrancois 4/11

Respostas:

9

A conclusão do comando (junto com outras coisas) é realizada através da conclusão da linha de leitura do bash . Isso opera em um nível um pouco mais baixo do que a "conclusão programável" usual (que é chamada apenas quando o comando é identificado e nos dois casos especiais que você identificou acima).

Atualização: a nova versão do bash-5.0 (janeiro de 2019) adiciona complete -Iexatamente esse problema.

Os comandos readline relevantes são:

complete (TAB)
       Attempt to perform completion on the text  before  point.   Bash
       attempts completion treating the text as a variable (if the text
       begins with $), username (if the text begins with  ~),  hostname
       (if  the  text begins with @), or command (including aliases and
       functions) in turn.  If none of these produces a match, filename
       completion is attempted.

complete-command (M-!)
       Attempt  completion  on  the text before point, treating it as a
       command name.  Command completion attempts  to  match  the  text
       against   aliases,   reserved   words,  shell  functions,  shell
       builtins, and finally executable filenames, in that order.

De maneira semelhante à mais comum complete -F, parte disso pode ser transferida para uma função usando bind -x.

function _complete0 () {
    local -a _cmds
    local -A _seen
    local _path=$PATH _ii _xx _cc _cmd _short
    local _aa=( ${READLINE_LINE} )

    if [[ -f ~/.complete.d/"${_aa[0]}" && -x  ~/.complete.d/"${_aa[0]}" ]]; then
        ## user-provided hook
        _cmds=( $( ~/.complete.d/"${_aa[0]}" ) )
    elif [[ -x  ~/.complete.d/DEFAULT ]]; then
        _cmds=( $( ~/.complete.d/DEFAULT ) )
    else 
        ## compgen -c for default "command" complete 
        _cmds=( $(PATH=$_path compgen -o bashdefault -o default -c ${_aa[0]}) )  
    fi

    ## remove duplicates, cache shortest name
    _short="${_cmds[0]}"
    _cc=${#_cmds[*]} # NB removing indexes inside loop
    for (( _ii=0 ; _ii<$_cc ; _ii++ )); do
        _cmd=${_cmds[$_ii]}
        [[ -n "${_seen[$_cmd]}" ]] && unset _cmds[$_ii]
        _seen[$_cmd]+=1
        (( ${#_short} > ${#_cmd} )) && _short="$_cmd"
    done
    _cmds=( "${_cmds[@]}" )  ## recompute contiguous index

    ## find common prefix
    declare -a _prefix=()
    for (( _xx=0; _xx<${#_short}; _xx++ )); do
        _prev=${_cmds[0]}
        for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
            _cmd=${_cmds[$_ii]}
             [[ "${_cmd:$_xx:1}" != "${_prev:$_xx:1}" ]] && break
            _prev=$_cmd
        done
        [[ $_ii -eq ${#_cmds[*]} ]] && _prefix[$_xx]="${_cmd:$_xx:1}"
    done
    printf -v _short "%s" "${_prefix[@]}"  # flatten 

    ## emulate completion list of matches
    if [[ ${#_cmds[*]} -gt 1 ]]; then
        for (( _ii=0 ; _ii<${#_cmds[*]} ; _ii++ )); do
            _cmd=${_cmds[$_ii]}
            [[ -n "${_seen[$_cmds]}" ]] && printf "%-12s " "$_cmd" 
        done | sort | fmt -w $((COLUMNS-8)) | column -tx
        # fill in shortest match (prefix)
        printf -v READLINE_LINE "%s" "$_short"
        READLINE_POINT=${#READLINE_LINE}  
    fi
    ## exactly one match
    if [[ ${#_cmds[*]} -eq 1 ]]; then
        _aa[0]="${_cmds[0]}"
        printf -v READLINE_LINE "%s " "${_aa[@]}"
        READLINE_POINT=${#READLINE_LINE}  
    else
        : # nop
    fi
}

bind -x '"\C-i":_complete0'

Isso permite que suas próprias seqüências de caracteres por comando ou prefixo sejam conectadas ~/.complete.d/. Por exemplo, se você criar um executável ~/.complete.d/loccom:

#!/bin/bash
echo localc

Isso fará (aproximadamente) o que você espera.

A função acima mede alguns comprimentos para emular o comportamento normal de conclusão do comando bash, embora seja imperfeito (particularmente a sort | fmt | columnbagagem duvidosa para exibir uma lista de correspondências).

No entanto , um problema não trivial com isso, ele pode usar apenas uma função para substituir a ligação à completefunção principal (chamada por TAB por padrão).

Essa abordagem funcionaria bem com uma ligação de chave diferente usada apenas para a conclusão de comandos personalizados, mas simplesmente não implementa a lógica de conclusão completa depois disso (por exemplo, palavras posteriores na linha de comando). Para isso, seria necessário analisar a linha de comando, lidar com a posição do cursor e outras coisas complicadas que provavelmente não devem ser consideradas em um script de shell ...

mr.spuratic
fonte
1

Não sei se entendi sua necessidade disso ...
Isso implicaria que o seu bash conhece apenas um comando começando com f.
Uma idéia básica de conclusão é: se for ambígua, imprima as possibilidades.
Portanto, você pode definir seu PATHdiretório apenas com este comando e desativar todos os bash builtins para obter esse trabalho.

De qualquer forma, também posso fornecer uma solução alternativa:

alias _='true &&'
complete -W foo _

Portanto, se você digitar _ <Tab>, será concluído o _ fooque é executado foo.

Mas, no entanto, alias f='foo'seria muito mais fácil.

m13r
fonte
0

Resposta simples para você seria

$ cd into /etc/bash_completion.d
$ ls

apenas os resultados básicos

autoconf       gpg2               ntpdate           shadow
automake       gzip               open-iscsi        smartctl
bash-builtins  iconv              openssl           sqlite3
bind-utils     iftop              perl              ssh
brctl          ifupdown           pkg-config        strace
bzip2          info               pm-utils          subscription-manager
chkconfig      ipmitool           postfix           tar
configure      iproute2           procps            tcpdump
coreutils      iptables           python            util-linux
cpio           lsof               quota-tools       wireless-tools
crontab        lvm                redefine_filedir  xmllint
cryptsetup     lzma               rfkill            xmlwf
dd             make               rpm               xz
dhclient       man                rsync             yum.bash
e2fsprogs      mdadm              scl.bash          yum-utils.bash
findutils      module-init-tools  service
getent         net-tools          sh

basta adicionar o programa desejado para concluir automaticamente e completar

unixmiah
fonte
2
Esses arquivos têm script de shell, alguns bastante complexos, se bem me lembro. Além disso, eles só completam argumentos quando você já digitou o comando ... Eles não completam o comando.
Derobert 25/09/14
Eu entendo o que você quer dizer. Quando você pode dar uma olhada neste documento: debian-administration.org/article/316/…
unixmiah
-3

Execute o comando abaixo para descobrir onde o binário mplayer está instalado:

which mplayer

OU use o caminho para o binário mplayer, se você já o conhece, no comando abaixo:

ln -s /path/to/mplayer /bin/mplayer

Idealmente, qualquer coisa digitada é pesquisada em todos os diretórios especificados na $PATHvariável.

Mathew Paret
fonte
2
Olá, Mathew, bem-vindo ao Unix e Linux. Acho que a pergunta do OP é um pouco mais complicada do que a resposta que você está sugerindo. Eu presumo que mplayerjá está em sua $PATHe eles querem algo como mp<tab>a produção mplayer, em vez de todos os binários que começam com mp(por exemplo, mpage mpcd mpartition mplayeretc.)
DRS