Як налаштувати завершення команди Bash?


24

В bash, досить просто налаштувати індивідуальне завершення аргументів команди за допомогою completeвбудованого. Наприклад, якщо для гіпотетичної команди із конспектом foo --a | --b | --cви могли б це зробитиcomplete -W '--a --b --c' foo

Ви також можете налаштувати завершення ви отримуєте , коли ви натискаєте Tabна порожній рядку , використовуючи complete -E, наприклад, complete -E -W 'foo bar'. Тоді натискання вкладки в порожньому запиті запропонує лише fooі bar.

Як налаштувати завершення команди в НЕ -empty підкажіть? Наприклад, якщо я сиджу:

anthony@Zia:~$ f

як налаштувати завершення, щоб натискання вкладки завжди було б завершено foo?

(Фактичний випадок, який я хотів би, - locTABlocalc. А мій брат, який запропонував мені запитати це, хоче, щоб це було з mplayer).


2
Чи розглядали ви просто альясінг locдо localc? Я пропоную альтернативи, тому що після доволі тривалого часу копання та пошуку я не знайшов способу налаштувати завершення башу таким чином. Це може бути неможливим.
jw013

@ jw013 Так, я деякий час шукав і нічого не знайшов. Я наполовину очікую, що хтось також запропонує перейти на zsh. Принаймні, якщо zsh це зробить, я можу комфортно відпочити, знаючи також наступну версію bash will. 😀
derobert

вам потрібно двічі натиснути TAB, і він виконає команди.
Флойд

1
Невже це не порушить усі інші завершення? Я маю в виду, навіть якщо це можливо, ви більше не бути в змозі отримати locate, locale, lockfileабо будь-який з інших розширень loc. Можливо, кращим підходом було б відобразити інший ключ до конкретного завершення.
тердон

1
чи можете ви навести приклад такого контексту (це призведе до бажаного ефекту loc<TAB>->localc)?
damienfrancois

Відповіді:


9

Завершення команди (разом з іншими речами) обробляється через завершення bash readline . Це працює на дещо нижчому рівні, ніж звичайне "програмоване завершення" (яке викликається лише тоді, коли команда ідентифікована, і два особливих випадки, які ви визначили вище).

Оновлення: новий випуск bash-5.0 (січень 2019 р.) Додає complete -Iсаме цю проблему.

Відповідними командами для читання рядків є:

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.

Подібно до більш поширених complete -F, деякі з них можна передавати функції за допомогою 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'

Це дозволяє ввімкнути власні гачки на рядок команди та префікса ~/.complete.d/. Наприклад, якщо ви створили виконуваний файл ~/.complete.d/locіз:

#!/bin/bash
echo localc

Це зробить (приблизно) те, що ви очікуєте.

Наведена вище функція переходить на деяку довжину, щоб імітувати нормальну поведінку команди bash, хоча вона недосконала (особливо сумнівне sort | fmt | columnперенесення для відображення списку збігів).

Однак нетривіальна проблема з цим може використовувати лише функцію для заміни прив'язки до основної completeфункції (за замовчуванням викликається TAB).

Цей підхід добре працюватиме з різними прив'язками ключів, що використовуються лише для виконання спеціальних команд, але він просто не реалізує логіку повного завершення після цього (наприклад, пізніші слова в командному рядку). Для цього знадобиться розбір командного рядка, обробка позиції курсору та інші складні речі, які, ймовірно, не повинні розглядатися в сценарії оболонки ...


1

Я не знаю, чи зрозумів я вашу потребу в цьому ...
Це означає, що ваш баш знає лише одну команду, починаючи з f.
Основна ідея завершення: якщо це неоднозначно, надрукуйте можливості.
Таким чином, ви можете встановити свій PATHкаталог лише у цій команді та відключити всі вбудовані файли bash, щоб отримати цю роботу.

Так чи інакше, я можу дати вам також такий спосіб вирішення:

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

Тож якщо ви введете _ <Tab>його, ви будете завершувати _ fooвиконання foo.

Але все-таки це alias f='foo'було б набагато простіше.


0

Проста відповідь для вас була б

$ cd into /etc/bash_completion.d
$ ls

лише основні результати

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

просто додайте бажану програму для автоматичного завершення до завершення роботи


2
Ці файли містять скрипт оболонки, який є досить складним, якщо я правильно пам'ятаю. Крім того, вони завершують аргументи лише після того, як ви вже ввели команду ... Вони не завершують команду.
дероберт

Я розумію, що ти маєш на увазі. Коли ви можете подивитися на цей документ: debian-administration.org/article/316/…
unixmiah

-3

Запустіть команду нижче, щоб знайти, де встановлений бінарний файл mplayer:

which mplayer

АБО використовуйте шлях до двійкового файлу mplayer, якщо ви знаєте його, в команді нижче:

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

В ідеалі все, що ви вводите, шукається у всіх каталогах, вказаних у $PATHзмінній.


2
Привіт, Метью, ласкаво просимо до Unix & Linux. Я думаю, що питання ОП трохи складніше, ніж відповідь, яку ви пропонуєте. Я припускаю, що mplayerвони вже є, $PATHі вони хочуть щось подібне mp<tab>виробляти mplayerзамість усіх бінарних файлів, які починаються з mp(наприклад, mpage mpcd mpartition mplayerтощо)
1414
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.