Solution for escaping '$' in completing variable on bash 4.2

bash 4.2系で作業しているときに,変数を経由してディレクトリにアクセスするときにタブキーで補完を行うと$がエスケープされてしまい,補完が効かなくなってしまった。この問題と対処方法を説明する。

Introduction

bash-4.2.0の対話シェルから,$HOME/tmpのファイルを確認しようと,$HOMEの後にタブキーで補完しようとすると$がエスケープされてしまう。

ls $HOME/T # <- Input Tab key
ls $HOME/Templates # expected
ls \$HOME/Templates # real

$がエスケープされてしまったがために,それ以降の補完が作動しない。補完は頻繁に使うため,煩わしい。

この問題は以下で報告・議論されている。

  1. readline – Bash variable expansion on tab complete – Stack Overflow
  2. Bug #778627 “bash completion now quotes shell variable reference…” : Bugs : bash package : Ubuntu

どうやらbash 4.2系特有の問題であり,それ以前のバージョンでは発生していないようだ。

2番目の記事(バグレポートのフォーラム )にbashのバージョンごとの対策の導入状況が議論されている。この記事を元に対策を解説する。

A.1. Use bash 4.2.29 or later

最初の解決策はbashのバージョンを4.2.29以上にあげることだ。

以下のコメントのとおり,このバグ自体はbash-4.2.29のパッチで解決したらしい。

SOLUTION: This was a bug in bash which is fixed in patch bash4.2-029. See
http://lists.gnu.org/archive/html/bug-bash/2012-07/msg00018.html
Comment #14 : Bug #778627 : Bugs : bash package : Ubuntu

GNU Bashのページからダウンロードして,実際にbash 4.2にbash42-029のパッチを当ててビルドすると問題が直っていることを確認できた。ただし,これだけでは以下2点の理由から解決できないことがある。

  1. bash-4.2.29以降でも発生した。
  2. 自分でbashをバージョンアップできないことがある。

自分が最初に問題を確認したFedora 19のbashは4.2.45だった。bash-4.2.29以降であればパッチがあたっていて問題が発生しないはずなのだが,何かの手違いかで解決していなかった。

インターネットに接続できなかったり,客先のPCで自由にソフトのダウンロードやインストールが許可されていない環境がある。

このことから別の対策をとる必要がある。

A.2. shopt -s direxpand

bashのdirexpandオプションを有効にして対処する。

以下にある通り,bash-4.2-3から導入されたshoptコマンドのdirexpandオプションで回避可能と提案されている。

This bug was fixed in 4.2-3 upload, and hence is fixed in quantal and raring.

However, there is a new option that one needs to set – direxpand.

direxpand
If set, bash replaces directory names with the results of word expansion when performing filename completion. This
changes the contents of the readline editing buffer. If not set, bash attempts to preserve what the user typed.
Comment #27 : Bug #778627 :Bug : bash package : Ubuntu

direxpandオプションを有効にすると,対話シェル上でタブキーでディレクトリやファイル名の補完するときに変数を展開する。以下に例を示す。

shopt -s direxpand
ls $HOME/T # Input Tab key
ls /home/senooken/Templates # $HOME is expanded to /home/senooken

この方法であれば,.bashrcに設定を書くか,対話シェル上で一度コマンドを実行して有効にするだけでいいので簡単だ。

欠点は以下2点となる。

  1. bash-4.0から4.2まではdirexpandオプションが導入されていないので使えない。
  2. 変数が常に展開されるので,コマンドラインが冗長になる。

変数を展開すると,コマンドラインが文字で埋め尽くされてしまい冗長になってしまうので,個人的にはdirexpandオプションはあまり使いたくない。しかし,bash 4.2に限ってはタブでの補完が効かないことのほうが問題なので,bash-4.2に限りこのオプションを使うようにする。

上記欠点を踏まえた設定例は以下となる。

[ "$BASH_VERSION%.*}" = 4.2 ] && shopt -s direxpand 2>&-

&&の手前の前半部分でbashのバージョンが4.2であることを判定している。&&以降の後半部分でdirexpandオプションを有効にしている。shoptコマンドはbashのバージョンが古い場合などで,オプション名が有効でなければ標準誤りに以下のようなメッセージを表示してしまう。

bash: shopt: direxpand: invalid shell option name

このメッセージを防ぐために,末尾の2>&-で標準誤りを閉じている。shoptの設定を.bashrcなどにまとめて書く場合は,以下のようにグループ化してエラーメッセージをまとめて抑制すれば個別に2>&-を書く手間を省くこともできる。

{
shopt -s option1
shopt -s option2
# Avoid completing $VAR -> \$VAR [ "${BASH_VERSION%.*}" = 4.2 ] && shopt -s direxpand # 4.2.3+
} 2>&-

Summary

bash-4.2系でのみ発生する補完時のバグとその対処方法について解説した。

bashはLinuxの標準シェルとして広く普及しているが,今回のような細かいバグがいくつか見つかる。機能追加に付随するバグなのである程度はしかたないのかもしれない。また見つけたら記録していく。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です