Как настроить завершение команды Bash?

24

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

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

Как настроить завершение команды в не пустом приглашении? Например, если я сижу по адресу:

anthony@Zia:~$ f

как настроить завершение так, чтобы нажатие вкладки всегда завершалось foo?

(Фактический случай, который мне нужен, это locTABlocalc. И мой брат, который побудил меня спросить об этом, хочет это с помощью mplayer).

derobert
источник
2
Рассматривали ли вы просто альясинг locк localc? Я предлагаю альтернативы, потому что после долгого времени копания и поиска я не нашел способа настроить завершение bash таким образом. Это может быть невозможно.
jw013
@ jw013 Да, я некоторое время искал и ничего не мог найти. Я наполовину ожидаю, что кто-то тоже предложит перейти на zsh. По крайней мере, если zsh сделает это, я могу спокойно отдыхать, зная, что будет и следующая версия bash. De
Дероберт
Вы должны нажать TAB дважды, и он выполнит команды.
Флойд
1
Разве это не сломает все другие завершения, хотя? Я имею в виду, даже если это возможно, вы больше не быть в состоянии получить locate, locale, lockfileили любой из других расширений loc. Возможно, лучшим подходом было бы сопоставить другой ключ с этим конкретным завершением.
Terdon
1
Можете ли вы привести пример такого контекста (который привел бы к желаемому эффекту loc<TAB>->localc)?
Дамьенфрансуа

Ответы:

9

Завершение команды (наряду с другими вещами) обрабатывается посредством завершения чтения bash . Это работает на несколько более низком уровне, чем обычное «программируемое завершение» (которое вызывается только после идентификации команды и двух особых случаев, которые вы определили выше).

Обновление: новая версия bash-5.0 (январь 2019) добавляет complete -Iименно эту проблему.

Соответствующие команды readline:

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 по умолчанию).

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

mr.spuratic
источник
1

Я не знаю, понял ли я вашу потребность в этом ...
Это означало бы, что ваш bash знает только одну команду, начинающуюся с f.
Основная идея завершения: если это неоднозначно, распечатайте возможные варианты.
Таким образом, вы можете установить PATHдиректорию, содержащую только эту команду, и отключить все встроенные команды bash, чтобы получить эту работу.

Во всяком случае, я могу дать вам также своего рода обходной путь:

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

Так что если вы наберете, _ <Tab>он будет завершен, _ fooкоторый выполняет foo.

Но тем не менее alias f='foo'было бы намного легче.

m13r
источник
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

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

unixmiah
источник
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и т. Д.)
drs