Bashコマンド補完をカスタマイズする方法は?


24

bashでは、completeビルトインを使用してコマンド引数のカスタマイズされた補完を設定するのは簡単です。たとえば、概要がの仮想コマンドの場合foo --a | --b | --c、次のようにできます。complete -W '--a --b --c' foo

また、などを使用Tabして、空のプロンプトでを押したときに表示される補完をカスタマイズすることもできます。次に、空のプロンプトでタブを押すと、とのみが提案されます。complete -Ecomplete -E -W 'foo bar'foobar

空でないプロンプトでコマンド補完をカスタマイズするにはどうすればよいですか?例えば、私が座っている場合:

anthony@Zia:~$ f

タブを押すと常に完了するように補完をカスタマイズするにはどうすればよいfooですか?

(私が望む実際のケースはlocTABlocalcです。そして、これを尋ねるように促した私の兄弟は、mplayerでそれを望んでいます)。


2
単にエイリアスlocを作成することを検討しましたlocalcか?かなり時間をかけて掘り下げて検索した後、この方法でbashの完了をカスタマイズする方法を見つけていないため、代替案を提案します。不可能な場合があります。
jw013 14

@ jw013ええ、私はしばらく探しましたが、何も見つかりませんでした。私は誰かがzshへの切り替えを提案することを期待しています。少なくともzshがそれをやれば、bashの次のバージョンも同様に安心して休むことができます。😀
derobert

Tabキーを2回押すとコマンドが完了します。
フロイド14年

1
しかし、それは他のすべての完了を壊しませんか?私はそれが可能であっても、あなたはもはや取得することができないだろう、意味locatelocalelockfile、または任意の他の拡張のloc。おそらく、より良いアプローチは、特定の完了に異なるキーをマップすることです。
テルドン

1
あなたはそのようなコンテキストの例を与えることができます(それは望ましい効果につながるでしょうloc<TAB>->localc)?
damienfrancois

回答:


9

コマンドの完了は(他のものと一緒に)bash readline completionを介して処理されます。これは、通常の「プログラム可能補完」(コマンドが識別されたときにのみ呼び出され、上記で特定した2つの特別な場合)よりもわずかに低いレベルで動作します。

更新: bash-5.0の新しいリリース(2019年1月)では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で呼び出されます)。

このアプローチは、カスタムコマンド補完だけに使用される別のキーバインドでうまく機能しますが、その後、完全な補完ロジックを実装しません(コマンドラインの後の単語など)。これを行うには、コマンドラインの解析、カーソル位置の処理、およびおそらくシェルスクリプトで考慮すべきでないその他の注意が必要です...


1


これに対するあなたの必要性を理解しているかどうかはわかりません... これは、bashがで始まるコマンドを1つだけ知っていることを意味しますf
補完の基本的な考え方は次のとおりです。曖昧な場合は、可能性を出力します。
したがってPATH、この1つのコマンドのみを含むディレクトリに設定し、すべての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

目的のプログラムをbash完了までの自動完了に追加するだけです


2
これらのファイルにはシェルスクリプトが含まれており、正しく覚えていればかなり複雑です。また、コマンドを既に入力した後にのみ引数を完了します...コマンドを完了しません。
デロバート14

何と言いたいのか分かった。あなたは、このドキュメントを見てとることができた場合:debian-administration.org/article/316/...を
unixmiah

-3

以下のコマンドを実行して、mplayerバイナリがインストールされている場所を見つけます。

which mplayer

または、次のコマンドで、mplayerバイナリを知っている場合は、そのパスを使用します。

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

理想的には、入力したものはすべて、$PATH変数で指定されたすべてのディレクトリで検索されます。


2
こんにちは、マシュー、UnixとLinuxへようこそ。OPの質問は、あなたが提案している答えよりも少し複雑だと思います。私推測がmplayer彼らの中にすでにあり$PATH、彼らはのような何かをしたいmp<tab>生成するmplayer代わりに、で始まるすべてのバイナリの、mp(例えば、mpage mpcd mpartition mplayerなど)
DRS
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.