AWS CloudShellをControl-Bできちんと1文字戻るように設定してみた

デフォルト状態のCloudShell上ではC-b (Ctrl+b)がtmuxのPrefix keyとして扱われるため、2回叩かないと1文字戻る操作になりません。tmuxのPrefix keyを変更することできちんと1文字戻るように設定してみました。
2024.03.26

はじめに

清水です。AWSを操作するうえで大変便利なAWS CloudShellなのですが、以前から「ターミナル内でのカーソル移動がうまくいかない!」という個人的に残念に感じている点がありました。具体的には「Control-Bでの1文字戻る移動」と「Control-Pでの1つ前の履歴を表示する操作」です。それぞれ矢印キー(カーソルキー/方向キー)の「」と「」の操作に対応するものですね。矢印キーでの操作は問題ないのですが、Control-BならびにControl-Pについては入力が無効になってしまうような動作になってしまいます。

このControl-BControl-Pの挙動などについて確認してみたところ、CloudShellであらかじめインストール・実行されているツールなどがこれらのキーを操作のために使用しており、キー操作を奪ってしまっているような状態であることが確認できました。具体的にはControl-Bはtmux、Control-Pは(おそらく)Dockerで使用されている、という状況です。

このうちControl-Bの操作については、tmuxのプレフィックスキーを変更することで「きちんと1文字戻る操作」に設定することができました。本エントリではこの設定内容についてまとめてみます。なお、Control-Pの操作についてはDokcerで使用しているようであることは確認できたものの、設定変更の実現には至りませんでした。

なおControl-B/Control-Pについては、それぞれControlキーを押しながらBもしくはPキーを押す操作です。Ctrl+b/Ctrl+pといった表記など様々な表現方法があるかと思いますが、本エントリでは以降C-b/C-pと表記することとします。

また前提として、本エントリの内容は筆者の環境(OSはmacOS、ブラウザはGoogle Chrome)での検証結果に基づきます。ほか環境では未検証であり、同様の動作をしない場合も考えられますがご了承ください。CloduShellの環境については、デフォルト状態のものに設定を追加するかたちで動作確認をしています。

AWS CloudShellデフォルトのC-bとC-pの操作について

まずはAWS CloudShellデフォルト状態でのC-bならびにC-pの操作について、筆者が残念に思う点をまとめていきます。

CloudShellデフォルト状態でのC-b

まずはC-bの操作についてです。通常であればターミナル上でカーソルが1文字(左へ)戻る移動を想定します。ですが、C-bを一度押しても左に移動しません。2回C-bを押してやっとカーソルが1文字ぶん左に戻る、という操作でした。戻りたいぶんの2倍のC-bを叩けば目的は達成できますが…… 地味にストレスが溜まります。なお、カーソルキーでは通常通り、一度を叩けば1文字文左にカーソルが移動するというぐあいです。

CloudShellデフォルト状態でのC-p

続いてはC-pの操作です。こちらも通常であれば、ターミナル上で1つ前の履歴を表示する操作を想定します。ですが一度C-pしても何も表示されません。2回めのC-pで2つ前の履歴が表示されます。また一度C-pして何も表示されないながらも、例えばC-aで行の先頭に移動するなどカーソル操作を行うと、そのタイミングで履歴が表示されます。一度めのC-pは無視される、もしくは該当する履歴が次のキー操作まで表示されない、というような挙動になってしまっているかと思います。

なお、こちらもカーソルキーではこのような挙動はなく、通常通り1つ前の履歴が表示されます。またこの無視されてしまうようなC-pの挙動、東京リージョンでは発生するのですが大阪リージョンでは発生しません。リージョンごとに事象の発生有無がわかれていそうです。

Controlキーでのターミナル操作はReadlineで実現されている

設定変更の話をする前に、このカーソル移動や履歴表示などターミナルの操作について簡単におさえておきましょう。ここまで当たり前のようにControlキーとなにかのキーの同時押しでターミナル上のカーソル移動や履歴表示などの操作ができるものとして扱ってきました。

そもそも前提として、CloudShellのターミナル上ではデフォルトのシェルであるBashが動作しています。そしてBashではデフォルトでReadline (GNU Readline)というライブラリが組み込まれており、このReadlineによってカーソル移動やコマンド履歴の表示・検索、コピー・アンド・ペースト(キルリング)などの機能が実現されています。

試しに--noeditingオプションを付与し、Readlineを使わない状態でbashを起動してみましょう。C-fC-nC-aとタイプしたのが以下の状態です。カーソル移動やコマンド履歴の表示といった操作ができないことが確認できますね。

このReadlineについて詳細は、以下bashマニュアルページの「READLINEライブラリ」の項目などをご確認ください。

上記にも記載がありますが、このReadlineのキーボードショートカットとしてデフォルトでEmacsのキーバインディングが採用されている、ということになります。なお、筆者は設定したことはないのですが、viのキーバインディングも利用可能なようです。

CloudShellをC-bできちんと1文字戻るように設定する

さて、いよいよ本エントリの主題であるCloudShell上でC-bをきちんと1文字戻るようにする設定を確認していきます。

CloudShell利用の際にはデフォルトでtmuxが動作している

まずはデフォルト状態のCloudShell環境で、C-bがどのように動作しているのかを確認します。

CloudShell上ではデフォルトでターミナルマルチプレクサのtmuxが起動、動作しています。CloudShellの利用にあたっては常に(ユーザ側でtmuxコマンドを実行しなくても)、tmux経由でターミナルを使っている状況となります。

このデフォルトで動作しているtmuxの設定について確認してみましょう。起動時に-fオプションで/var/lib/amazon/cloudshell/tmux.confを読み込み、status lineを非表示にするよう設定されています。それ以外については特に設定されておらず、~/.tmux.confファイルなども存在しないようです。そのため、tmux操作のためのプレフィックスキー(Prefix key)はデフォルトのC-bに設定されている状態となります。

試しにCloudShell上でC-b "(ペインを上下に分割)、C-b o(ペインを移動)、C-b t(時計の表示)を行ってみました。いずれもtmuxのとしての動作がそのままターミナル上に反映されていますね。

CloudShellのデフォルト状態ではC-bはtmuxのプレフィックスキーとして動作しているようです。C-bで期待しているカーソルを1文字(左へ)戻る移動をさせるためには2回C-bを叩く必要がありました。一度めのC-bはtmux側でプレフィックスキーとして認識されてしまい、続く2回目のC-b操作で(対応するtmuxの操作がないため)bashに操作が渡り、はじめて期待する操作である1文字戻る移動になっているのではないか、と考えられます。

なお、筆者はふだんからtmuxを使用してはいるのですが、10年近くプレフィックスキーはC-tに設定して使用してきました。このこともあり、CloudShellにtmuxがプリインストールされていることは知っていたのですが、デフォルトで動作している(常にtmux経由でのターミナル操作になっている)とは気が付きませんでした。(status lineが表示されていたら気がついたかとは思うのですが。)

そんな中、たまたまC-b ,の操作を行ったところ、ターミナル画面下部にtmuxのrename-windowの表示が現れました。そこからtmuxが動作していることの発見に至ったわけです。

tmuxのプレフィックスキーを.tmux.confで変更する

CloudShell上でのC-b操作はtmuxが奪ってしまっていることがわかりました。tmuxは~/.tmux.confファイルでプレフィックスキーを変更できます。C-bではなく別のキーに変更してしまいましょう。私は使い慣れているC-tを新たなプレフィックスキーとして設定しました。

~/.tmux.conf

# Changing the default prefix key:
set-option -g prefix C-t
unbind-key C-b
bind-key C-t send-prefix

上記の内容の~/.tmux.confファイルを作成後、tmux source-file ~/.tmux.confを実行することで設定が反映され、tmuxのプレフィックスキーがC-bからC-tになります。そしてC-bはこれまでのようにtmuxに奪われることなく、素直にきちんと1文字戻る動作をしてくれるようになります。やった!

しかしCloudShellの再起動(Restart)を行うと、~/.tmux.confファイルは残っているのに、C-bが再びtmuxのプレフィックスキーになってしまっている状態です。

もういちどtmux source-file ~/.tmux.confすればいいのですが、CloudShell利用の際に都度都度打つのも手間ですし面倒ですし忘れてしまいます。

.bashrcに処理を追加してCloudShell再起動時にtmuxのプレフィックスキーが変更された状態にする

CloudShellが再起動などされても、利用時にはtmuxのプレフィックスキーが常に変更されているよう設定することを考えます。(ただし、~/.tmux.confを含むホームディレクトリが残っていることを前提とします。)

~/.tmux.confがあり、利用の際にtmux source-file ~/.tmux.confを実行する、ということですので、この処理を.bashrcで実施してしまいましょう。.bashrcに以下を追記します。

~/.bashrcに以下を追記

# Read tmux configuration file
tmux source-file ~/.tmux.conf

これで、CloudShellを再起動してもプレフィックスキーがデフォルトのC-bからC-tに変更された状態のtmuxで操作ができます。ブラボー!

CloudShellのC-p操作を探る

tmuxのプレフィックスキーをCloudShell起動時に変更するよう設定することで、C-bできちんと1文字戻るようになりました。このままC-pについてもきちんと1つ前の履歴を表示するよう設定してみたかったのですが、残念ながら現段階でこのきちんとしたC-p操作については実現できていません。

調査した結果、このC-pについてはDocker(コンテナ)側で操作を奪ってしまっているであろうことが確認できました。以下、それについての根拠と、設定変更にトライしたものの実現できなかったことの記録をまとめておきたいと思います。

C-p操作を奪っているものはなにか

まずC-pの操作について、tmuxのプレフィックスキーと同じように何かしらのツール等で操作を奪ってしまっていることを考えました。Googleで「Ctrl+P 履歴 表示されない」などで検索すると、例えば以下のページのような、「DockerでのControl-P 2回押し問題」という情報が見つかります。

DockerではC-p C-qがデタッチキー(detach key)となっており、実行中のコンテナからデタッチして離れるのに使用されるとのことです。(attach — Docker-docs-ja 19.03 ドキュメント)お恥ずかしながら筆者はDockerまわりに疎く、このデタッチ操作自体を知りませんでした。

実際にCloudShell上でC-p C-qをしてみましょう。read escape sequenceと表示され、ターミナルから切断されます。なにか操作をすることでPreparing your terminal...となり再度接続されますが、CloudShell環境からいちど離れる動作を行っていることがわかります。

このコンテナからのデタッチ操作C-p C-qが、C-pを奪ってしまっている(C-p入力後、C-qが続くか待機してチェックしている)ような状況と推測できます。ここで、CloudShell自体がDocker(ないしコンテナ)上で動作しているのでしょうか、これが確認できればC-qを奪っているのはDockerと確信することができそうです。たしか先日CloudShellがDocker対応したなんて話がありましたが、それはCloudShell上でDockerが動く話で、CloudShellの実行基盤としてはDocker(コンテナ)が動作しているのかな、と調べてみます。非公式ながら以下の情報が見つかりました。繰り返しになりますが、筆者はDockerなどコンテナまわりに疎く、さらに英語の情報で理解が少し怪しいのですが、CloudShell自体もDocker(コンテナ環境)上で動作している、と理解しております。

~/.docker/config.jsonでdetachKeysの変更を試みるも

CloudShell上でC-p C-qで環境から離れる動作をすること、そしてCloudShell自体がDockerで動いているようであることがわかりました。それなら一般的な「DockerでのControl-P 2回押し問題」と同様の対策が取れるのでは?と、Dockerのででタッチキーの変更を試みてみます。~/.docker/config.jsondetachKeysで設定するかたちですね。ひとまず参考にしたサイトにならい、C-\を設定してみることにします。

~/.docker/config.json

{
    "detachKeys": "ctrl-\\"
}

上記のように~/.docker/config.jsonファイルを作成後、bashを起動したりexitしてターミナルに接続し直したりするも、C-p C-qの動作は変わりません。CloudShellを再起動しても同様です。

Dockerないしコンテナの動作をきちんと考えるとわかるかと思うのですが、あくまでデタッチキーの設定変更~/.docker/config.jsonはコンテナを動かしているホスト上で設定が必要で、CloudShellのホームディレクトリなどコンテナ内の環境に設定しても反映はされない、というように理解しています。(とはいえ、とりあえず安直にやってみた備忘録として、ここに記録しておきます。)

一部リージョンではC-pが正しく動作する謎

さて、C-p操作については上記の通り設定変更は実現ならずという状況なのですが、この調査の際に一つおもしろい現象を見つけたので記録しておきます。どうやらCloudShellでDockerが動いている?、けどCloudShellがDocker対応したのは東京リージョンなど一部リージョンのみで、例えば大阪リージョンではまだ対応していなかったはず、と大阪リージョンのCloudShellでC-p動作を確認してみました。(この動作確認自体は、Dockerを動かす環境なのか、Dockerで動いている環境なのか、ということがごちゃまぜになってしまっている点、ご注意ください。あくまで試行錯誤の段階で発見した事象の記録としてまとめておきます。)

すると、大阪リージョンではC-pできちんと「1つ前の履歴を表示する操作」ができました。またC-p C-qをしてもターミナルから切断されるといった動作は行われません。大阪リージョンではCloudShell上でのDocker実行がまだサポートされていないことが関係しているのでしょうか。つまりCloudShellを実行している基盤側が、その基盤上でDockerをサポートしているかしていないか、といった観点から違いがあり、そこがC-p操作に影響しているのでは、などと想像しています。Docker未サポートのほかリージョンで動作確認するとなにか掴めるかもしれません。しかし筆者がDockerなどコンテナまわりに疎いこともあり、これ以上の調査はいったん諦めることとしました……。

まとめ

AWS CloudShellではデフォルト状態でtmuxを経由してターミナルを使用しています。Control-Bがtmuxのプレフィックスキーとして動作してしまうため、別プレフィックスキーを定義してControl-Bできちんと「1文字戻る移動」をするように設定してみました。またControl-Pはコンテナからのデタッチキーの一部として使用されているようです。こちらはControl-Pできちんと「1つ前の履歴を表示する操作」に設定することはできませんでした。さしあたりはControl-PしたあとにControl-Aと操作することで、1つ前のコマンド履歴が行頭にカーソル移動された状態で表示される、という状態になるので、これでしのごうかと思います。(矢印キーに指を伸ばすのがどうしても億劫なんですよね。)