2009年4月7日火曜日

bash-completion

zshの補完は魅力でもbashを捨てられないという人も多いだろう。自分もそのひとり。その悩みを解決してくれるのが、bash-completionである。

例えば、manの引数にコマンド名を補完しようとしても[Tab]では補完されない(コマンド名補完は[Alt-!]で可能だが)といった経験があると思う。bash-completionはそのコマンドの文法(オプション)に合わせて補完してくれるので[Tab]を叩いていればよい。

ただし、manの場合は存在するコマンドから補完するのでなく、manが存在するコマンド名を補完しようとするみたいなので、初回はその検索に時間を多少要する。
それが、rpm や yum のパッケージ名になるとDBが大きいので顕著になるので注意。キャッシュに残っていれば速いがそうでない場合は、"rpm -qa"とかをしているのと同じスピードになってしまうので。

次にtarコマンドの場合、foo.tbz2 と hoge.tgz の2つのみがカレントにあるとき
$ tar zxf [Tab]
とファイル名の頭文字さえも打たずに[Tab]しても zオプションなので".tbz2"は対象外となり、ちゃんと hoge.tgzの方が補完される。
また、hello.sとhello.oの2つみがある場合も
$ vi [Tab]
でhello.sを補完してくれる。

svnやgit、mercurialなどのサブコマンドが必要な場合でもそれぞれに合った補完をしてくれる。
また、killのようにPIDが必要な場合でも存在するPIDが補完される(PIDは順に振られるため補完対象が決定するまでタイプしてると結局全部タイプしてたとかということもあるので、あまり役に立たないけど)。

極めつけは、make。Makefile内のターゲットを都度確認して補完してくれるのだ。
「clean」ターゲットがあれば"make c[Tab]"で補完してくれるが、無い場合は補完しないといった具合に。更にMakefileからincludeされているファイルも解析するので、「menuconfig」や「uImage」なども補完してくれる(その分補完に時間が掛かるが)。
もちろん、その他コマンドに対してもキメ細かな補完をしてくれる。


インストールは以下で完了。
$ sudo yum -y install bash-completion

ただし、デフォルトの状態ではお節介過ぎる補完があるので修正をする。

"~/De[Tab]"とすると、通常は"~/Desktop/"と補完されるが、bash-completionの場合"/home/okap/Desktop/"とお節介なほどに補完されてしまう。「~」の方がスッキリしていて見やすいのでそこをホームディレクトリ名に展開されると長ったらしくなり困るのだ。

$ sudo vim /etc/bash_completion
...
# This function expands tildes in pathnames
#
_expand()
{
    [ "$cur" != "${cur%\\}" ] && cur="$cur\\"

    # expand ~username type directory specifications
    if [[ "$cur" == \~*/* ]]; then
        eval cur=$cur

    elif [[ "$cur" == \~* ]]; then
        cur=${cur#\~}
        COMPREPLY=( $( compgen -P '~' -u $cur ) )
        return ${#COMPREPLY[@]}
    fi
}
...
この部分でevalしてるのがお節介な箇所。なのでbashの"何もしない"(組込み)コマンド「:」に置き換える。
        eval cur=$cur
             ↓
        #eval cur=$cur
        :

これで新しいセッションでは、「~」がホームディレクトリ名に展開されなくなる。



3 件のコメント:

  1. はじめまして.
    >「~」の方がスッキリしていて見やすいのでそこをホームディレクトリ名に展開されると長ったらしくなり困るのだ。
    ごもっとも!と思い,さっそく設定させていただきました^^

    しかし,scpなどでリモート名:ファイル名といった形($ scp hoge.com:~/Desktop/)をとると思うのですが,ここの~はこのままだとまだ展開されてしまうみたいです.

    もしかして解決法をご存じではないでしょうか?
    よろしくお願い致します.

    返信削除
  2. 確かにそうですね。
    しかし、scpの場合top_dirはリモートのユーザのhome_dirになっているので、パス指定には「~」は不要です。($ scp hoge.com:Desktop/ で OK)
    この指定でもちゃんとリモートファイル名も補完され、かつウザったい /home/user/ は付加されませんでした。

    ちなみに、各コマンドの固有部分は /etc/bash_completion.d/以下にあります。
    scpの場合は、/etc/bash_completion.d/ssh が指している実態ファイル内で「~」展開をしているようです。

    返信削除
  3. なるほど~
    大変参考になりました.ありがとうございます.

    返信削除