鯨飲馬食

いろいろつまみ食いで勉強したことのメモ書き

Dockerコンテナ内からターミナルを識別する

プログラムからターミナルの種類を判定する方法について、longcat で対応したやり方を説明します。

TERM_PROGRAM環境変数による識別

TERM_PROGRAM環境変数には利用しているターミナルのプログラム名がセットされます。例えば

  • iTerm2 の場合は iTerm.app
  • Terminal.app の場合は Apple_Terminal

という値がセットされる。ネイティブで実行している場合には、プログラム中でこの環境変数の値に応じた処理をさせることで、ターミナルの自動判定を行えます。実際、longcat に Terminal.app 対応を入れた際にはその方法で判定していました。

Dockerコンテナの中から

dockerコンテナ内のプログラムにターミナルに応じた処理をさせたい場合にはどうすればよいでしょうか?デフォルトでは環境変数はコンテナ内のプログラムには引き継がれず、docker run の -e オプションでこのTERM_PROGRAMを明示的に指定すれば伝達できます。しかしこれでは自動判定を行なっているとは言えません。

f:id:yoichi22:20200811115101p:plain

DA2による識別

ターミナルに特定のエスケープシーケンスを送信することで、ターミナルの種別に応じた応答を得ることができます。DA2 (Secondary Device Attributes)では、ESC [ > c を送信します。試しにprintfコマンドを使ってやってみると、iTerm2 の場合

$ printf "\x1b[>c"
$ 0;95;0c

と "0;95;0c" という応答が得られます。Terminal.app の場合は以下のようになります。

$ printf "\x1b[>c"
$ 1;95;0c

この応答を読み取って、読み取った値で判定すればターミナルに応じた処理ができます。ターミナル自身が応答するので、この方法は docker run -it してttyを割り当てていればコンテナ内からも判定に使えます。

実装

既にDA1 (Primary Device Attributes)を使ってsixel対応を判定するコードがあったので、それを参考にして実装して動作確認したのですが、うまく動作するときとしない場合がありました。得られた応答をプリントしてデバッグすると、応答の途中までしか読み込まれていないことがわかり、少し待ちを入れることで対処しました。

github.com

猫を表示するだけのプログラムから、ターミナルの知識をまた一つもらいました。面白い。

参考文献