Como personalizar a conclusão do comando Bash?

21

No bash, é fácil configurar a conclusão personalizada de argumentos de comando usando o complete integrado. Por exemplo, se, para um comando hipotético com uma sinopse de foo --a | --b | --c , você poderia fazer complete -W '--a --b --c' foo

Você também pode personalizar a conclusão obtida ao pressionar Tab em um prompt vazio usando complete -E , por exemplo, complete -E -W 'foo bar' . Em seguida, pressionar a guia no prompt vazio sugeriria apenas foo e bar .

Como personalizo a conclusão do comando em um prompt non -empty? Por exemplo, se estou sentado em:

anthony@Zia:~$ f

como personalizar a conclusão para que a guia de pressionamento sempre seja concluída em foo ?

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

    
por derobert 05.08.2014 / 14:29

4 respostas

8

A conclusão do comando (juntamente com outras coisas) é tratada via bash readline completion . Isso opera em um nível um pouco menor do que a "conclusão programável" usual (que é invocada somente quando o comando é identificado e os dois casos especiais identificados acima).

Atualização: a atual versão dev do bash v5 adiciona complete -I exatamente para este problema.

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 ao mais comum complete -F , parte disso pode ser passado 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 habilita seus próprios ganchos por string de comando ou prefixo em ~/.complete.d/ . Por exemplo. se você criar um executável ~/.complete.d/loc com:

#!/bin/bash
echo localc

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

A função acima vai a alguns comprimentos para emular o comportamento normal do completamento do comando bash, embora seja imperfeito (particularmente o duvidoso sort | fmt | column carry-on para exibir uma lista de correspondências).

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

Essa abordagem funcionaria bem com uma vinculação de chave diferente usada apenas para conclusão de comando personalizada, mas ela simplesmente não implementa a lógica de conclusão completa depois disso (por exemplo, palavras posteriores na linha de comando). Fazer isso exigiria 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 ...

    
por 23.07.2015 / 18:36
1

Eu não sei se entendi sua necessidade por isso ...
Isto implicaria que o seu bash só conhece um comando que começa com f .
Uma idéia básica de conclusão é: se for ambígua, imprima as possibilidades.
Então você poderia definir seu PATH para um diretório contendo apenas este comando e desabilitar todos os builtins bash para obter este trabalho.

De qualquer forma, posso dar a você também uma espécie de solução alternativa:

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

Portanto, se você digitar _ <Tab> , ele será concluído para _ foo , que executa foo .

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

    
por 08.08.2014 / 12:41
0

Resposta simples para você seria

$ cd into /etc/bash_completion.d
$ ls

apenas as saídas básicas

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

adicione seu programa desejado para preenchimento automático para concluir o bash

    
por 25.09.2014 / 05:33
-3

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

which mplayer

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

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

Idealmente, qualquer coisa que você digita é pesquisada em todos os diretórios especificados na variável $PATH .

    
por 15.10.2014 / 16:21