まえおき
数ヶ月前にMacBook Airを購入しましたが、シェルはBashを使っています。
Bashに対するキー入力は、Readlineが解釈してコマンドラインに反映します。
ある日、Readlineの数あるキーバインドのうちCtrl-Oが動いていないことに気づき、一時しのぎでEsc-Oに割当ててしばらく使っていたんですが、やっぱり使いにくかったので調査して直した記録になります。
環境
- MacBook Air (macOS Catalina)
- GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19)
- GNU Readline version 8.0 (たぶん)
普段使っている端末はiTerm2ですが、Mac標準の端末で確かめてもやはり同じように動きませんでした。
期待する本来の動き
Ctrl-Oには、既定ではReadlineのoperate-and-get-next
が割当てられています。man bash
に、operate-and-get-next
についての次のような説明が見られます。
operate-and-get-next (C-o)
Accept the current line for execution and fetch the next line relative to the current line from the history for editing. Any argument is ignored.
実際の挙動としては、本来は↓のような動きになります。
入力したls -a
がまず実行され、実行後のプロンプトに最後に実行したコマンド(つまりls -a
)を最初から表示した状態で待機します。
場面としては、似たようなコマンドを続けて実行する場合や、正しく動くことを都度確認しながら少しずつコマンドを打ちこんでいきたい場合(長いsed
やちょっと複雑めなfor文など)に使うと便利です。
これをやりたくてCtrl-Oを何回入力しても、端末はうんともすんとも言わず、困ったわけです。
原因
普段仕事で使っているWSLやUbuntuのBashでは問題なく動いているので、Mac特有の問題か…?とか思っていたんですが、どうやら原因は、stty
がCtrl-Oの入力をReadlineに届く前に奪い取ってしまっているためのようでした。
stty
には現在の設定を表示する--all
(-a
)オプションがあります。これを実行すると、次のように出力されました(今回の内容と直接関係がない部分は省略しています)。
# all current settings in human-readable form
$ stty --all
# (省略...)
werase = ^W; lnext = ^V; discard = ^O; status = ^T; min = 1; time = 0;
# (省略...)
↑のdiscard = ^O
が悪さをしている箇所です。
ReadlineがCtrl-Oをoperate-and-get-next
として解釈する前に、stty
が入力を受け取ってdiscord
として解釈してしまっている、という機序のようです。
解決
stty
のdiscard
が何をするのかはよくわかりませんが、とりあえず直します。
~/.bashrc
に次のように追記し、Ctrl-Oを送ってもstty discard
で邪魔されないようにします。
stty discard undef
これで動くようになりました。めでたし。
参考
今回の不具合について調べていたら、Bashで履歴のインクリメンタルサーチがきかない不具合を直す記事を見かけ、「もしかしたら今回の不具合も…」と思ったらドンピシャでした。