Linuxホスト(Omarchy OS)からUbuntuにSSH接続して作業していたら、 大文字を打つたびに文字ではなくエスケープシーケンスが表示される 症状に遭遇した。原因はmodifyOtherKeysのプロトコル不整合で、最終的に.bashrcで明示的に無効化することに落ち着いた。記録として残しておく。
TL;DR
~/.bashrcの末尾に以下を追記:
# Disable terminal modifyOtherKeys
case "$-" in *i*) printf '\e[>4;0m' ;; esac
症状
SSH先のプロンプトでcat -vして大文字を入力すると、こうなる:
~ ❯ cat -v
^[[27;2;86~ ← Shift+V を打った
a^[[27;2;65~ ← a, Shift+A を打った
通常はVやAが表示されるはずが、生のエスケープシーケンスが流れてくる。
エスケープシーケンスを読み解く
^[[27;2;86~という形は、本来こうあるべきだったエスケープシーケンスの「中身だけ」が見えている状態:
ESC [ 27 ; 2 ; 86 ~
↑ ↑ ↑
prefix modifier code
(固定) (2 = Shift) (86 = ASCII 'V')
ASCIIで読み解くと:
| 数値 | 文字 |
|---|---|
| 86 | V |
| 65 | A |
つまり「Shift + V」「Shift + A」という打鍵情報が、文字に変換されず生のシーケンスのまま流れていた。
原因: modifyOtherKeysのプロトコル不整合
これはmodifyOtherKeys(xterm拡張キーエンコーディング)が、ローカル端末では有効化されているのに、リモート側のbash/readlineで解釈されていない、という現象。
[ローカル端末 (Ghostty)] [SSH] [Ubuntu側 bash]
modifyOtherKeys=2 が ON → そのまま中継 → readline はこのプロトコルを知らない
Shift+V を ESC[27;2;86~ → 文字列としてそのまま表示
として送信
切り分け
Step 1: TERMを揃える(失敗)
最初に疑ったのはTERM環境変数の不一致。リモートで設定し直してみた:
export TERM=xterm-256color
→ 直らない。
Step 2: ローカル側を疑う
SSHしない状態で、ローカル端末でそのままcat -vを実行:
~ ❯ cat -v
^[[27;2;86~
SSHを経由していないローカル単体でも、すでにエスケープが出ている。 → 端末エミュレータ自体が無条件でmodifyOtherKeysを送っていると確定。SSH/リモートの問題ではなく、ローカル端末側の挙動の問題だった。
TERMを変えても直らなかったのは筋が通る話で、TERMの問題は 「相手にどう見えているか」、modifyOtherKeysの問題は 「相手に何を喋るか」 という別レイヤーの話だから。
Step 3: 端末を特定する
使っている端末エミュレータを特定:
~ ❯ ps -o comm= -p $(ps -o ppid= -p $$)
ghostty
解決策: .bashrcで明示的に無効化
~/.bashrcの末尾に以下を追記:
# Disable terminal modifyOtherKeys
case "$-" in *i*) printf '\e[>4;0m' ;; esac
\e[>4;0mはxterm系の「modifyOtherKeysを0(無効)に戻す」シーケンスcase "$-" in *i*)でインタラクティブシェル限定にしている。これがないと、スクリプト実行時にも余計なエスケープを吐いて副作用が出る
シェル起動のたびに端末に対して「もう拡張で送るな」と通告する形。
なぜ端末側ではなく .bashrc で無効化するか
Ghostty自体の設定でmodifyOtherKeysを切ることもできるが、Ghosttyは状況や他の設定の影響でmodifyOtherKeysが再びONになるケースがある。 一方、 シェル側で起動時に明示的に無効化 しておけば、端末側の状態に依存しない。
Ghosttyを使う構成では、.bashrcでの無効化をベースラインに置くのがベストだと思う。
まとめ
- 大文字入力で
^[[27;2;XX~が出るのはmodifyOtherKeysのプロトコル不整合 - Ghostty利用時は
.bashrcでの無効化フォールバックを置いておくのがベスト