コマンドライン、ネイティブスクリプト、およびアプリケーションでのbash補完の使用。 パート2

habrでのbashの完了については、すでにここで書きましたが 、最後でさえ、独自のスクリプトのオートコンプリートのセットアップについて説明することを約束しました。



しかし、すでに1年半であり、個人的には私の手は私の継続に達しませんでした。 しかし、この名誉ある義務はhabraiser infthiによって引き継がれました。彼に代わって公開します。







オートコンプリートの基本を学んだモデルに近いモデルの問題を検討します。 一番下の行は次のとおりです。







3つのサブコマンドを持つスクリプトがあります。 これらのコマンドの1つであるworkは、この例では考慮されていません;残りのコマンドには次のサブコマンドがあります。 履歴はインポートまたはエクスポートを行うため、これらの各コマンドには、プロジェクト名とフラグの下の値のペアを渡す必要があります。 ヘルプは、仕事、歴史、ヘルプについて伝えることができます。



実際、自動補完の仕組みについて説明します。 bash用の関数が記述され、既に入力された引数が渡され、それらに基づいて、可能な追加オプションが生成されます。 この関数(それを_my_commandと呼びましょう)は、特定のコマンド(この場合、スクリプトと呼ばれるスクリプトを実行するので、登録はスクリプト用です)に魔法の完全なコマンドで登録されます。



complete -F _my_command script







ここで最も興味深い部分は、引数を処理し、使用可能なパラメーターのリストを返すこの関数を作成することです。

まず第一に、オートコンプリートを操作するための特別な変数の存在についてman bashから学ぶことができます。 以下を使用します。



回答

これは、bashが可能な追加を取得する配列です。



COMP_WORDS

これは、すでに入力された引数を含む配列です。 それらを分析することで、どの追加オプションが提供されるべきかを理解できます。



COMP_CWORD

これは前の配列のインデックスであり、現在編集中の引数の位置を示します。



では、これらの変数に基づいて入力を分析してみましょう。最初の引数が入力された場合は、それを補うようにしてください

  _my_command(){ # ,      COMPREPLY=() #    ,   ,    . cur="${COMP_WORDS[COMP_CWORD]}" #    subcommands_1="work history help" #    - .     . if [[ ${COMP_CWORD} == 1 ]] ; then #   ,     COMPREPLY=( $(compgen -W "${subcommands_1}" -- ${cur}) ) #some magic return 0 #COMPREPLY ,   fi }
      
      







上記の呼び出しとともにこの関数を記述して、たとえば./complete.shなどのスクリプトで完了する場合、現在のコンソールで実行します(もちろん、実験のために新しいbashを起動してから強制終了します)。 ./complete sh、および「script」と入力し、Tabキーを2回押すと、bashは追加のオプションを提供します。

 $ script help history work
      
      





したがって、たとえばwoなどのサブコマンドの入力を開始してTabキーを押すと、オートコンプリートが発生します。



ただし、スクリプトで使用される魔法の仕組みについてはまだ説明していません。つまり、



  COMPREPLY=( $(compgen -W "${subcommands_1}" -- ${cur}) ) #some magic
      
      







ここでは、組み込みのbash compgenユーティリティを使用して、返されたオプションのリストに記入します。



このユーティリティは、引数のすべての可能な値のリストと引数の現在の入力部分を受け取り、入力された部分を補うことができる値を選択します。 引数の入力部分は-の後に渡され、可能な値のリストを使用すると、すべてがより興味深いものになります。 この場合、スクリプトの特定の単語リストに対して(-Wフラグで示されるように)可能な値が取得されます(つまり、上記の例では、subcommands_1 =“ work history help”から)。 ただし、他のフラグ(たとえば、-d)を指定すると、compgenはマシン上の既存のディレクトリに基づいて補完するか、-f-その後ファイルに補完します。



生成されるものを確認できます。

 $ compgen -W "qwerty qweasd asdfgh" -- qwe qwerty qweasd
      
      







したがって、オートコンプリートの候補のさまざまなリストを生成できます。 たとえば、手元のタスクの場合、(履歴のインポートとエクスポートのために)可能なプロジェクトのリストが必要です。 私の場合、各プロジェクトには「$ {HOME} / projects」にディレクトリがあるため、候補は次のように選択できます。

COMPREPLY=($(compgen -W "`ls ${HOME}/projects`" -- ${cur}))







したがって、オートコンプリートは単純に機能します。現在の引数の前に入力された引数を確認し、目的のコマンドを呼び出す構文の検討から進んで、現在の引数の可能な値のリストを生成します。 その後、申請者のリストは、引数のすでに入力された部分によってフィルタリングされ、bashに渡されます。 申請者が一人の場合は、彼をバッシュして代用します。 すべてがシンプルです。



結論として、冒頭に示したモデルの自動補完の私の不器用な実装:



 _my_command() { COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" subcommands_1="work history help" #    subcommands_history="import export" #   history if [[ ${COMP_CWORD} == 1 ]] ; then #         COMPREPLY=( $(compgen -W "${subcommands_1}" -- ${cur}) ) return 0 fi subcmd_1="${COMP_WORDS[1]}" #       ,        case "${subcmd_1}" in # ,      work) COMPREPLY=() #     return 0 ;; history) if [[ ${COMP_CWORD} == 2 ]] ; then # script history;   import  export COMPREPLY=( $(compgen -W "${subcommands_history}" -- ${cur}) ) return 0 fi #     ,  :    subcmd_2="${COMP_WORDS[2]}" if [[ ${COMP_CWORD} == 3 ]] ; then #        . COMPREPLY=($(compgen -W "`ls ${HOME}/projects`" -- ${cur})) return 0 fi case "${subcmd_2}" in #        .     ,      -    ,  -     . import) case "${COMP_WORDS[COMP_CWORD-1]}" in -src) COMPREPLY=($(compgen -d -- ${cur})) #      return 0 ;; -file) COMPREPLY=($(compgen -f -- ${cur})) #     return 0 ;; *) COMPREPLY=($(compgen -W "-src -file" -- ${cur})) #   return 0 ;; esac ;; export) #     -o,    -      ,  ,  -     if [[ ${COMP_WORDS[COMP_CWORD-1]} == "-o" ]] ; then COMPREPLY=($(compgen -f -- ${cur})) return 0 fi COMPREPLY=($(compgen -W "-o" -- ${cur})) return 0 ;; *) ;; esac ;; help) #    help      ,   . COMPREPLY=( $(compgen -W "${subcommands_1}" -- ${cur})) return 0 ;; esac return 0 } complete -F _my_command script
      
      







最も重要なことは、スクリプトを継続的にbashに接続することです。 これは、.bashrcでスクリプトへの呼び出しを(不器用に)書くか、/ etc / bash_completionを介して(通常、このようなファイルがある場合は)実行します。 2番目のケースでは、スクリプトを/etc/bash_completion.dに配置する必要があり、そこからのすべてのスクリプトは/ etc / bash_completionからフックされます。



PS:私は、ユーザーinfthiのカルマに正のフィードバックを残す必要があることを思い出します



All Articles