Bash komut tamamlamasını nasıl özelleştirebilirim?


24

Kısaca, completeyerleşik komutları kullanarak özelleştirilmiş komut argümanlarının tamamlanmasını ayarlamak yeterince kolaydır . Örneğin, bir özeti olan varsayımsal bir komut için foo --a | --b | --c,complete -W '--a --b --c' foo

Ayrıca bastığınızda olsun tamamlama özelleştirebilirsiniz Tabbir de boş kullanarak istemi complete -E, örneğin complete -E -W 'foo bar'. Sonra boş istemde sekmeye basmak sadece foove önerecektir bar.

Nasıl bir de komut tamamlama özelleştiririm olmayan -boş istemi? Örneğin, eğer oturuyorsam:

anthony@Zia:~$ f

Tamamlama nasıl özelleştirilir, böylece sekme her zaman tamamlanmış olur foo?

(İstediğim gerçek durum locTABlocalc. Ve bunu sormamı isteyen erkek kardeşim onu ​​mplayer ile istiyor).


2
Basitçe başka ad locvermeyi düşündün mü localc? Alternatifler öneriyorum, çünkü oldukça uzun bir süre kazı ve arama yaptıktan sonra bash tamamlamayı bu şekilde özelleştirmenin bir yolunu bulamadım. Bu mümkün olmayabilir.
jw013

@ jw013 Evet, bir süre aradım ve hiçbir şey bulamadım. Ben de birisinin zsh'ye geçmesini önermesini bekliyorum. En azından, eğer zsh yaparsa, bash'ın bir sonraki sürümünün de olacağını bilerek rahatça dinlenebilirim. De
derobert

iki kez SEKME tuşuna basmanız gerekir, komutları tamamlar.
Floyd

1
Bu olsa bütün diğer tamamlamaları bozmaz mı? Bunun mümkün olsa bile, artık almak mümkün olacaktır, yani locate, locale, lockfileveya herhangi bir diğer açılımları loc. Belki de daha iyi bir yaklaşım söz konusu tamamlama için farklı bir anahtarın haritalandırılması olabilir.
terdon

1
Böyle bir bağlamın bir örneğini verebilir misiniz (olması istenen etkiyi doğurabilir loc<TAB>->localc)?
damienfrancois

Yanıtlar:


9

Komutun tamamlanması (diğer şeylerle birlikte) bash okuma satırı tamamlaması ile gerçekleştirilir . Bu, normal "programlanabilir tamamlama" dan biraz daha düşük bir seviyede çalışır (yalnızca komut tanımlandığında ve yukarıda tanımladığınız iki özel durum çağrılır).

Güncelleme: bash-5.0'ın yeni çıkması (Ocak 2019) complete -Ibu soruna kesinlikle katılıyor .

İlgili okuma satırı komutları:

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.

Daha yaygın olana benzer şekilde, bunun bir complete -Fkısmı kullanılarak bir işleve devredilebilir 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'

Bu, kendi başına komut veya önek dizginin kancalarına takılmasını sağlar ~/.complete.d/. Örn ~/.complete.d/loc: ile çalıştırılabilir bir dosya oluşturursanız

#!/bin/bash
echo localc

Bu (kabaca) beklediğiniz şeyi yapacak.

Yukarıdaki işlev, normal bash komut tamamlama davranışını taklit etmek için bazı uzunluklara gider, ancak kusursuz olmasına rağmen (özellikle sort | fmt | columnbir eşleşme listesi görüntülemek için şüpheli bir şekilde devam eder).

Bununla birlikte , bunun önemsiz bir sorunu sadece ana completeişleve bağlamayı değiştirmek için bir işlev kullanabilir (varsayılan olarak TAB ile çağrılır).

Bu yaklaşım sadece özel komut tamamlama için kullanılan farklı bir anahtar bağlama ile iyi çalışır, ancak bundan sonra tam tamamlama mantığını uygulamaz (örneğin komut satırında daha sonra kelimeler). Bunu yapmak komut satırının ayrıştırılmasını, imleç konumunun ele alınmasını ve muhtemelen bir kabuk komut dosyasında düşünülmemesi gereken diğer zor şeyleri gerektirir.


1

Bunun için ihtiyacınızı karşılayıp karşılamadığımı bilmiyorum ...
Bu, sizin bashınızın yalnızca bir emir ile başladığını bildiği anlamına gelir f.
Temel bir tamamlama fikri şudur: eğer belirsiz ise, olasılıkları yazdırın.
Böylece, PATHyalnızca bu komutu içeren bir dizine ayarlayabilir ve bu çalışmayı elde etmek için tüm bash yerleşiklerini devre dışı bırakabilirsiniz.

Her neyse, sana bir tür geçici çözüm de verebilirim:

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

Yani yazarsanız hangi _ <Tab>yürüteceğini tamamlayacaktır ._ foofoo

Ancak, yine de alias f='foo', çok daha kolay olurdu.


0

Basit cevap sizin için olurdu

$ cd into /etc/bash_completion.d
$ ls

sadece temel çıktılar

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

İstediğiniz programı bash tamamlanana kadar otomatik tamamlamaya ekleyin


2
Bu dosyaların içlerinde kabuk betiği var, doğru hatırlıyorsam bazıları oldukça karmaşık. Ayrıca, yalnızca komutu bir kez yazdığınızda argümanları tamamlarlar ... Komutu tamamlamazlar.
derobert

Ne demek istediğini anlıyorum. Bu doktora ne zaman bakabilirsiniz: debian-administration.org/article/316/…
unixmiah

-3

Mplayer binary'in nerede kurulu olduğunu bulmak için aşağıdaki komutu çalıştırın:

which mplayer

VEYA aşağıdaki komutta, bildiğiniz takdirde mplayer binary yolunu kullanın:

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

İdeal olarak, yazdığınız her şey $PATHdeğişkende belirtilen tüm dizinlerde aranır .


2
Merhaba Mathew, Unix ve Linux'a hoş geldiniz. OP'nin sorusu, önerdiğiniz cevaptan biraz daha karmaşık. Sanırım mplayerzaten kendileri de var $PATHve onlarla başlayan tüm ikili dosyalar yerine (örneğin, vb.) mp<tab>Üretmek gibi bir şeyler istiyorlarmplayermpmpage mpcd mpartition mplayer
drs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.