2026年9月13日

2026年9月13日

bashのcompletionで補完機能をカスタマイズする方法(complete・compgenで自作コマンドに補完を付ける)

bash の補完は、組み込みコマンドの complete で「どのコマンドに、どんな候補を出すか」を登録し、候補の絞り込みに compgen を使うことで自由にカスタマイズできます。自作の運用スクリプトにサブコマンドやオプションの補完を付けたり、特定のディレクトリのバックアップファイルだけを候補に出したりすることも、数十行の関数で実現できます。この記事では、補完の仕組み、簡単な登録方法から関数を使った本格的な補完の書き方、設定ファイルの置き場所、思いどおりに動かないときの確認方法までを順に説明します。

補完の仕組みと、まず確認すること

Tab キーを押したときの候補は、次の2つの層で決まります。

  • **bash 本体の complete / compgen**: どのコマンドにどの補完ルールを使うかを登録する組み込みコマンドです。追加のパッケージは要りません。
  • bash-completion パッケージ: git、systemctl、apt など多数のコマンド用の補完定義と、補完を書くための補助関数をまとめたものです。多くのディストリビューションでは標準で入っているか、bash-completion という名前のパッケージで導入できます。

現在どんな補完が登録されているかは、complete -p で確認できます。

complete -p | head
complete -p git      # git に登録されている補完を表示
bash --version       # bash のバージョン

macOS に標準で入っている bash は 3.2 と古く、この記事で使う mapfile などが使えません。macOS で試す場合は Homebrew などで新しい bash と bash-completion(bash 4 以降向けの 2.x 系)を入れてください。

いちばん簡単な補完:complete -W

候補が固定の単語なら、complete -W で一覧を渡すだけで補完が付きます。

complete -W "start stop restart status" myservice

この状態で myservice st と入力して Tab を押すと、start、stop、status が候補に出ます。ただし、この方法は何番目の引数でも同じ候補を出すため、「1つ目はサブコマンド、2つ目以降はファイル名」のような区別はできません。

ファイル名やディレクトリ名を補完したいだけなら、-f(ファイル)や -d(ディレクトリ)を使えます。

complete -d mybackup-dir     # ディレクトリ名だけを補完
complete -f -X '!*.sql' myimport   # .sql ファイルだけを補完

-X はパターンに一致する候補を除外するオプションで、先頭に ! を付けると「一致しないものを除外」、つまり一致するものだけを残す意味になります。

関数で本格的な補完を書く:complete -F

引数の位置や直前の単語によって候補を変えたい場合は、補完用の関数を書いて complete -F で登録します。bash は Tab が押されるたびにこの関数を呼び出し、関数が配列 COMPREPLY に入れた値を候補として表示します。

関数の中では次の変数が使えます。

  • COMP_WORDS: 入力中のコマンドラインを単語に分けた配列
  • COMP_CWORD: カーソルがある単語の番号(コマンド名が 0)
  • COMP_LINE / COMP_POINT: コマンドライン全体の文字列と、カーソルの位置

また、関数には引数として「コマンド名」「補完中の単語」「その直前の単語」が $1、$2、$3 で渡されます。

例:WordPress の運用スクリプトに補完を付ける

ここでは、wpdeploy という自作スクリプトに次のような補完を付ける例を示します。

  • 1つ目の引数は backup、restore、deploy、status のいずれか
  • --env の後は staging か production
  • restore の後は、/var/backups/wp にある .tar.gz ファイル名
  • - で始まる入力には、オプションの一覧
# ~/.local/share/bash-completion/completions/wpdeploy
_wpdeploy() {
  local cur="${COMP_WORDS[COMP_CWORD]}"
  local prev="${COMP_WORDS[COMP_CWORD-1]}"
  local subcommands="backup restore deploy status"
  local options="--env --dry-run --help"
  local backup_dir="/var/backups/wp"

  COMPREPLY=()

  # 直前の単語によって候補を切り替える
  case "$prev" in
    --env)
      mapfile -t COMPREPLY < <(compgen -W "staging production" -- "$cur")
      return 0
      ;;
    restore)
      mapfile -t COMPREPLY < <(cd "$backup_dir" 2>/dev/null && compgen -f -X '!*.tar.gz' -- "$cur")
      return 0
      ;;
  esac

  # - で始まる入力にはオプションを出す
  if [[ "$cur" == -* ]]; then
    mapfile -t COMPREPLY < <(compgen -W "$options" -- "$cur")
    return 0
  fi

  # 1つ目の引数ならサブコマンドを出す
  if (( COMP_CWORD == 1 )); then
    mapfile -t COMPREPLY < <(compgen -W "$subcommands" -- "$cur")
  fi
}
complete -F _wpdeploy wpdeploy

ポイントは次のとおりです。

  • compgen -W "候補" -- "$cur" は、候補の中から入力中の文字列で始まるものだけを出力します。-- を入れているのは、$cur が - で始まるときにオプションと誤解されないようにするためです。
  • COMPREPLY=( $(compgen ...) ) という書き方もよく見かけますが、候補に空白が含まれると分割されてしまいます。mapfile -t で1行ずつ配列に入れるほうが安全です。
  • restore の候補は cd してから compgen -f しているため、フルパスではなくファイル名だけが表示されます。この場合、スクリプト側ではファイル名を $backup_dir と結合して扱う前提になります。
  • cd はプロセス置換 <( ... ) の中、つまり別のプロセスで実行されるので、補完を実行してもシェルのカレントディレクトリは変わりません。

補完定義をどこに置くか

作った関数は、置き場所によって読み込まれるタイミングが変わります。

  • **~/.bashrc に直接書く、または source する**: 最も単純で、自分のユーザーだけに効きます。ただし、シェルを起動するたびに読み込まれます。
  • **~/.local/share/bash-completion/completions/コマンド名**: bash-completion 2.x のユーザー用ディレクトリです。ファイル名をコマンド名と同じにしておくと、そのコマンドで初めて Tab を押したときに自動で読み込まれます(遅延読み込み)。環境変数 BASH_COMPLETION_USER_DIR や XDG_DATA_HOME を設定している場合は場所が変わります。
  • **/usr/share/bash-completion/completions/**: システム全体向けで、パッケージが入れる場所です。手作業で置くとパッケージ更新との区別がつきにくくなるため、自作の定義は管理者用に /usr/local/share/bash-completion/completions/ を使うか、構成管理ツールで配布するのがおすすめです。
  • **/etc/bash_completion.d/**: 古くからある場所で、今も読み込まれますが、シェル起動時にまとめて読み込まれるため起動が遅くなる原因になります。

ファイルを置いたら、新しいシェルを開くか source で読み込んでから試してください。

bash-completion の補助関数を使う

bash-completion が入っている環境では、補完を書くための補助関数が使えます。代表的なのが _init_completion で、cur、prev、words、cword を設定し、= や : を含む単語の扱いも整えてくれます。

_wpdeploy() {
  local cur prev words cword
  _init_completion || return
  # 以下、上の例と同じ処理
}

bash-completion の新しいバージョンでは補助関数の名前が _comp_ で始まるものに整理されつつあり、_init_completion は互換のために残されています(2026年10月現在)。自分の環境で使える関数は、インストールされた bash_completion ファイル(多くは /usr/share/bash-completion/bash_completion)で確認できます。bash-completion が入っていない環境でも動かす必要があるなら、上の例のように COMP_WORDS を直接使う書き方のほうが移植性は高くなります。

補完の表示を好みに合わせる(readline の設定)

候補の表示方法は、補完ルールではなく readline の設定で変えられます。~/.inputrc に次のように書くと、使い勝手が変わります。

$include /etc/inputrc
set completion-ignore-case on
set show-all-if-ambiguous on
set colored-stats on
  • completion-ignore-case: 大文字と小文字を区別せずに補完します。
  • show-all-if-ambiguous: 候補が複数あるとき、Tab を1回押しただけで一覧を表示します。
  • colored-stats: ファイルの種類に応じて候補に色を付けます。

先頭の $include は、システム全体の設定を引き継ぐためのものです。これを書かずに ~/.inputrc を作ると、ディストリビューションが用意したキー設定が効かなくなることがあります。

思いどおりに動かないときの確認

  1. 登録されているか: complete -p wpdeploy で、自分の関数が登録されているかを確認します。何も表示されなければ、ファイルが読み込まれていません。
  2. 関数の中身: set -x を実行してから Tab を押すと、補完関数の実行過程が表示されます。確認が終わったら set +x で戻します。
  3. **: や = を含む候補**: bash は COMP_WORDBREAKS に含まれる文字で単語を区切るため、host:port のような候補が途中から補完されることがあります。bash-completion の _init_completion や __ltrim_colon_completions で対処できます。
  4. 登録の解除: 試行錯誤で設定が混乱したら、complete -r wpdeploy で一度解除してから読み込み直します。

なお、WP-CLI には公式の補完スクリプトが用意されているため、wp コマンドの補完は自作するより公式のものを使うほうが、コマンドの追加にも追従でき保守が楽です。

まとめ

  • 固定の候補なら complete -W、位置や直前の単語で候補を変えるなら complete -F と補完関数を使う
  • 補完関数では COMP_WORDS と COMP_CWORD から状況を読み取り、compgen で絞った結果を COMPREPLY に入れる
  • 候補は mapfile -t COMPREPLY < <(compgen ... -- "$cur") の形で入れると空白を含んでも壊れない
  • 自作の定義は ~/.local/share/bash-completion/completions/コマンド名 に置くと遅延読み込みされる
  • 表示の好みは ~/.inputrc の readline 設定で調整する
  • 動かないときは complete -p で登録を確認し、set -x で関数の動きを追う

お気軽にご相談ください

お見積りへ お問い合わせへ
▲