web-dev-qa-db-ja.com

bashスクリプトに独自のオートコンプリートを含めることはできますか?

コマンドと引数をオートコンプリートするbashの機能を利用する方法を説明する多くのリソース( 12 、)があります。しかし、これらのリソースはすべて、ユーザーの~/.bash_profileまたは/etc/bash_completion.d/*にコードを追加する必要がありますが、スクリプトとその使用可能な補完を自己完結させる方法はありますか?粗雑で不完全な例として:

〜/ bin/script-with-integrated-autocomplete.sh

#!/usr/bin/env bash

function _completions {
  complete ...
}

if [ "$1" == "completions" ]; then
  _completions
  exit 0
fi

# (Whatever the script really does goes here.)
# ...
# ...

(この質問の文脈での)取引ブレーカーは、上記の例でも、完了に従事するために~/bin/script-with-integrated-autocomplete.sh completionsのようなものを.profileに追加する必要があることです。

単一のbashスクリプト(ファイル)が独自の補完を宣言し、呼び出し時にbashにそれらを認識させる方法があります(理想的には追加のシステムまたは環境設定なしで)?

4
beporter

質問へのコメントで述べたように、Bashは、以前の構成がなく、現在の環境を変更せずにコマンドのコマンドラインを完了することはできません。 Bashはファイルをのぞきませんし、明示的に実行またはソースしない限り、ファイルを解釈しようとしません。

この件についての私の読みは、互換性とリソースの問題のため、このような機能がすぐにBashに組み込まれる可能性は低いということです。シェルとしては、さまざまな目的でさまざまな異なるシステムで確実に実行することを目的としていますさまざまなユーザーの好みに適応する。開発者とメンテナーは一人しかいない1

Bashはオートコンプリートを実装するツールを提供しますが、コンプリーションジェネレーターは設計上、外部機能として意図されているようです。実際、あなたが探しているものは、既存のツールと少量の作業で簡単に実装できます。

サンプルスクリプトfoo

#!/bin/bash

# Setting up completion for foo. Only if the script is sourced
if [ "$0" != "$BASH_SOURCE" ]; then
    _foo() {
        local cur
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        COMPREPLY=( $(compgen -W 'bar baz' -- "$cur") )
        return 0
    }
    complete -F "_foo" "foo"
    return
fi

# The body of foo -- what it does when executed
echo foo "$*"
# ...

bash-completion を使用すると、fooへのシンボリックリンクを動的に読み込まれたユーザー補完を格納するディレクトリに追加することで、自己完結型オートコンプリートを有効にできます。

ln -s /path/to/foo ~/.local/share/bash-completion/completions/

正確なパスは異なる場合があります。システムでその値を確認する方法については、bash-completionのFAQを参照してください。

Bash-completionがない場合、1つのアプローチは、特定の条件を満たす場合にコマンド(に対応するファイル)をソースするデフォルトの完了関数を定義することです。ここでは、連想配列をホワイトリストとして使用しています(システム上のすべてのコマンドを盲目的に調達することを避けるため)。このコードを.bashrc(または同等のもの、つまりインタラクティブシェルが起動されるたびに現在の環境でソースされるファイル)に追加する必要があります。

_default_completion () {
    # Do nothing when completion is invoked for an empty command
    if [ "$1" = "" ]; then
        return 1
    fi
    # Get the command path, exit with failure if it can't be found
    cmd=$(type -p "$1") || return 1
    if [ "${_compwhitelist["${cmd##*/}"]}" = y ]; then
        # Source the file corresponding to the command; on success,
        # return 124 to trigger a new autocompletion attempt
        source "$cmd" && return 124
    fi
}
complete -D -F _default_completion -o bashdefault -o default

# The list of commands with embedded completion
declare -A _compwhitelist
_compwhitelist[foo]=y

設定ファイルを編集する必要のない解決策を求めていましたが、ある程度の設定が常に必要であることに注意してください。ユーザーが気付かない場合でも、その大部分はディストリビューション/パッケージのメンテナーによって行われます。

1参照: Bashホームページ Chet RameyのWebサイトにあり、関連するBash [〜#〜] faq [〜#〜] で「A1」をポイントします。

1
fra-san