SSH 端末に systemd OSC 3008 の文字化けが出るときの簡単な対処法

SSH で Ubuntu などの Linux システムへ接続したときに systemd OSC 3008 の文字化けが出る場合の対処法を整理。.bashrc でリモートセッションを判定して抑止する方法と、SSH クライアントのログイン後コマンドで個別に消す方法を紹介する。

SSH で Ubuntu、Kubuntu、または比較的新しい systemd ベースの Linux システムへ接続すると、コマンドプロンプト付近に奇妙な制御文字が表示されることがあります。手がかりになる文字列は OSC 3008、systemd context、__systemd_osc_context_* などです。通常はコマンド実行には影響しませんが、端末表示が汚れ、ログをコピーするときに余計な文字列が混ざります。

この問題は、特定の SSH クライアントだけに限定されるものではありません。WindTerm、組み込み端末、古い端末エミュレーター、OSC シーケンスの対応が不完全なクライアントでは、本来端末が解釈すべき制御シーケンスがそのまま表示されることがあります。

よくある原因は、リモートシステムが Bash 環境で systemd の OSC コンテキストフックを読み込んでいる一方、現在の SSH クライアントがそれらのシーケンスを正しく処理できていないことです。対処の考え方は単純です。フック関数が存在することを確認したうえで、それらを空の関数に上書きし、PS0 を空にします。

以下では 2 つの方法を紹介します。1 つ目はリモートの ~/.bashrc に書く方法で、SSH セッション全体を一括で処理したいサーバーに向いています。2 つ目は SSH クライアントの「ログイン後に実行するコマンド」に書く方法で、特定のクライアントやセッションだけに効かせたい場合に向いています。

方法 1:.bashrc で SSH セッションを判定して抑止する

この方法のロジックは、現在のセッションが SSH リモートセッションであり、systemd が注入した OSC フック関数が実際に存在する場合だけ、関連関数を空関数に上書きし、PS0 を空にするというものです。

次のブロックをリモートサーバーの ~/.bashrc の末尾に追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# SSH 接続を検出したとき、systemd OSC 3008 などの端末制御シーケンスの文字化けを抑止する
if [[ -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]; then
    # systemd の OSC 関数が実際に読み込まれている場合だけ上書きする
    if declare -f __systemd_osc_context_precmdline >/dev/null; then
        __systemd_osc_context_precmdline() { :; }
        __systemd_osc_context_common() { :; }
        __systemd_osc_context_escape() { :; }
        PS0=""
    fi
fi

保存したら SSH にログインし直すか、現在の shell で次を実行します。

1
source ~/.bashrc

文字化けの原因が systemd の OSC フックであれば、新しい shell に入った後は通常表示されなくなります。

特定クライアントにも対応したい場合

一部のクライアントは独自の環境変数を設定します。たとえば WindTerm では TERM_PROGRAM=WindTerm が設定される場合があります。この判定も残したい場合は、少し広めに次のように書けます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# WindTerm または SSH 接続を検出したとき、systemd OSC 3008 などの端末制御シーケンスの文字化けを抑止する
if [[ "$TERM_PROGRAM" == "WindTerm" || -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]; then
    # systemd の OSC 関数が実際に読み込まれている場合だけ上書きする
    if declare -f __systemd_osc_context_precmdline >/dev/null; then
        __systemd_osc_context_precmdline() { :; }
        __systemd_osc_context_common() { :; }
        __systemd_osc_context_escape() { :; }
        PS0=""
    fi
fi

通常の SSH では、SSH_CLIENTSSH_TTYSSH_CONNECTION の判定だけで十分です。TERM_PROGRAM を加えるのは、一部クライアントの識別方法も拾うためです。

この書き方が比較的安全な理由

この設定には 2 つの制限があります。

1 つ目はセッション判定です。

1
[[ -n "$SSH_CLIENT" || -n "$SSH_TTY" || -n "$SSH_CONNECTION" ]]

これらの変数は通常 SSH ログインセッションでだけ現れるため、主にリモートログインに作用し、ローカルのデスクトップ端末を不用意に変えることはありません。

2 つ目は関数の存在確認です。

1
declare -f __systemd_osc_context_precmdline >/dev/null

これは安全弁です。現在の shell が __systemd_osc_context_precmdline をすでに読み込んでいる場合だけ、関連関数を上書きします。systemd OSC の仕組みがない環境では、この設定は余計な処理をしません。

実際に上書きするのは次の関数です。

1
2
3
4
__systemd_osc_context_precmdline() { :; }
__systemd_osc_context_common() { :; }
__systemd_osc_context_escape() { :; }
PS0=""

これにより、関連フックが内容を出力しなくなります。PS0 は Bash がコマンドを読み取った後、実行する直前に展開するプロンプト変数です。一部の OSC シーケンスは PS0 や類似の仕組みを通じて挿入されるため、ここで一緒に空にします。

方法 2:SSH クライアントのログイン後コマンドに入れる

リモートサーバーの ~/.bashrc を変更したくない場合は、修正コマンドを SSH クライアントの「ログイン後に実行するコマンド」に入れます。多くの端末や SSH クライアントには似た機能があり、名前は次のような場合があります。

  1. Command executed after authentication
  2. Post-login command
  3. Remote command after login
  4. ログイン後に実行するコマンド

次の 1 行を設定します。

1
__systemd_osc_context_precmdline() { :; }; __systemd_osc_context_common() { :; }; __systemd_osc_context_escape() { :; }; PS0=""

クライアントが自動実行後に新しいプロンプト行へ移るための改行を要求する場合は、そのクライアントの構文に従って改行を追加します。たとえば WindTerm の Command executed after authentication では次のようにできます。

1
__systemd_osc_context_precmdline() { :; }; __systemd_osc_context_common() { :; }; __systemd_osc_context_escape() { :; }; PS0="" \n \n

2 つの \n は、クライアントにクリーンアップコマンドを実行させ、その後新しいプロンプト行へ移らせるためのものです。この書き方は、認証後の自動コマンドに対応する WindTerm のようなクライアントに向いており、Kubuntu 24.04 環境で動作確認されています。

この方法は影響範囲が小さいのが利点です。そのクライアント、そのセッションで接続した場合だけクリーンアップが実行され、サーバー側の shell 設定を変更する必要がありません。

どちらを選ぶか

そのサーバーを主に自分だけが使っている、またはすべての SSH クライアントでこの文字化けを避けたいなら、方法 1 がおすすめです。~/.bashrc に書いておけば、WindTerm、Windows Terminal、Tabby、Xshell、その他の SSH クライアントでも同じように処理できます。

共有サーバーである、または特定のクライアントだけで回避したいなら、方法 2 がおすすめです。リモート設定を変更せず、他のユーザーの shell 環境にも影響しません。

まず方法 2 で問題が解決することを確認してから、方法 1 を ~/.bashrc に入れるか決めるのもよいです。

注意点

どちらの方法も systemd の OSC 出力フックを抑止するだけです。systemd サービスを変更するわけではなく、SSH ログイン自体にも影響しません。

ただし、OSC 3008 に対応したモダンな端末を使っていて、コマンドコンテキスト、作業ディレクトリ、システム状態の表示に依存している場合は、抑止後にそれらの拡張表示が消える可能性があります。通常の SSH 運用、開発機へのログイン、サーバー管理では大きな問題にならないことが多いです。

また、最初からグローバルな /etc/bash.bashrc にこの上書きを書くのはおすすめしません。マシン上の全ユーザーに必要だと確認できている場合を除き、個人環境では ~/.bashrc、特定クライアント向けには SSH クライアントのログイン後コマンドを優先してください。

短いまとめ

SSH で Linux に接続したあと systemd OSC 3008 の文字化けが出る場合は、次の順番で対応できます。

  1. サーバーを変更したくない場合: SSH クライアントにログイン後のクリーンアップコマンドを設定する。
  2. 自分のサーバーの場合: SSH セッション判定のブロックを ~/.bashrc に入れる。
  3. 共有サーバーの場合: まずクライアント側の方法で確認し、その後 shell 設定へ広げるか判断する。

要点は、SSH セッション内だけで systemd の OSC フック関数を確認し、存在する場合は空関数に上書きして PS0 を空にすることです。これで文字化けを抑えつつ、通常の端末環境への影響をできるだけ小さくできます。

记录并分享
Hugo で構築されています。
テーマ StackJimmy によって設計されています。