web-dev-qa-db-ja.com

カスタムコマンドのBashオートコンプリートサブディレクトリ

私はこれを機能させるために何時間も努力しましたが、どこにも近づいていないように感じます。

Mac OS Xターミナルからディレクトリ名をタブで入力して、ワークフローを短縮しようとしています。

Gitサブモジュールでコマンドを自動的に実行する内部ツールをbobと呼びましょう。私がやりたいのは、サブディレクトリ名をオートコンプリートすることです。問題は、これらのサブディレクトリが静的な場所に存在することです。

   project directory
   ->public
     ->submodules
       ->a-module
       ->another-module
       ->other-module
       ->some-module

コマンドは、次のようにプロジェクトの最上位ディレクトリから実行されます。

bob start bugfix anothermodule

現在行っている「anothermodule」を完全に入力するのではなく、タブのオートコンプリートを機能させたいと思います。私たちはこのワークフローを1日に何度も使用しており、数秒を短縮することは素晴らしいことであり、タイプミスを回避します。

最終目標は次のとおりです。

bob start bugfix an<tab>

オートコンプリートします

bob start bugfix anothermodule

CDPATHを試しましたが、これはcdコマンドではなく、カスタムコマンドのみであるため、これが私が求めているものではないことにすぐに気付きました。

私が持っています bash_completionインストールされていますが、一般的にbashはあまり得意ではありません。それが解決策の一部になる可能性がある場合に備えて、これに言及してください!

3
Gedrick Wilson

これは Kamilの優れた回答 の拡張であり、各サブディレクトリ名の-module部分からハイフンを取り除きます。

bugfix)
    curdir=$(pwd)
    cd public/submodules/ 2>/dev/null && _filedir -d
    # Strip the hyphen from `-module` in each sub-directory name.
    for ((i=0; i<${#COMPREPLY[@]}; i++)); do
        COMPREPLY[$i]="${COMPREPLY[$i]/-module/module}"
    done
    cd "$curdir"
    ;;

このセクションを改善する余地はまだあります。ハイフンを入力する必要がある場合があります。つまり、 a-Tabamoduleamoduleのどちらかを選択する代わりに、anothermoduleまで完了するようにします(ハイフンは完了関数によって削除されます)。

1

そうです、_bash_completion_は解決策です。私のUbuntuに何があるかをお話しできます。 Macでも少なくとも似ているといいのですが。

_/etc/bash_completion_をソースとする_/usr/share/bash-completion/bash_completion_ファイルがあります。

ディレクトリ_/usr/share/bash-completion_には、completionsサブディレクトリもあります。これはスクリプトの場所です。 ejectスクリプトを見てみましょう。

_# bash completion for eject(1)                             -*- Shell-script -*-

_eject()
{
    local cur prev words cword
    _init_completion || return

    case $prev in
        -h|--help|-V|--version|-c|--changerslot|-x|--cdspeed)
            return
            ;;
        -a|--auto|-i|--manualeject)
            COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
            return
            ;;
    esac

    if [[ $cur == -* ]]; then
        COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) )
        return
    Elif [[ $prev == @(-d|--default) ]]; then
        return
    fi

    _cd_devices
    _dvd_devices
} &&
complete -F _eject eject

# ex: ts=4 sw=4 et filetype=sh
_

ドキュメントを読まなくても、少しリバースエンジニアリングして、ケースに合わせることができます。私たちにとって、上記のコードの最も重要な部分は、_-a_、_--auto_ _-i_または_--manualeject_の後にonまたはoffのいずれかが存在する可能性があることを示しています。あなたの場合、bugfixの後に、ディレクトリ名が必要です。

(またはそうではありません。_another-module_ディレクトリがありますが、anothermodule引数を使用します。今のところ、タイプミスだと思います。そうでない場合は、 Anthonyの答え が正しい方向を示していると思います。 。)

どういうわけか、必要なこれらのディレクトリを抽出して_'on off'_を置き換えることができますが、既存のコードを利用する方がよい場合があります。 cdと入力すると、bashはディレクトリをオートコンプリートし、スペースや特殊文字があってもうまく機能します。それでは、_cd()関数を見てみましょう。 _/usr/share/bash-completion/bash_completion_にあるので、ここに貼り付けません。次に、この関数の重要な部分は__filedir -d_呼び出しであるようです。 _filedir()が定義される直前に、次のようになります。

_# This function performs file and directory completion. It's better than
# simply using 'compgen -f', because it honours spaces in filenames.
_

ビンゴ。

そこで、ejectスクリプトをbobという名前でコピーし、次の形式に編集しました。

_# bash completion for bob                             -*- Shell-script -*-

_bob()
{
    local cur prev words cword curdir
    _init_completion || return

    case $prev in
        bob)
            COMPREPLY=( $( compgen -W 'start stop foo bar' -- "$cur" ) )
            return
            ;;
        start)
            COMPREPLY=( $( compgen -W 'bugfix' -- "$cur" ) )
            return
            ;;
        bugfix)
            curdir=$(pwd)
            cd public/submodules/ 2>/dev/null && _filedir -d
            cd "$curdir"    
            ;;
    esac

} &&
complete -F _bob bob
_

既存のbashセッションはこのファイルに気付かないため、新しいbashを実行してテストする必要があります。

はい、それは迅速で汚いブードゥープログラミングです、ごめんなさい。

_public/submodules/_に降りて戻ってきたことに注意してください。通常、私はサブシェルを使用します:

_export -f _filedir
( cd … && _filedir -d )
_

しかし、__filedir_はこのようには機能しないようです。

2