mizdra's blog

ぽよぐらみんぐ

iTerm2 で `cat /dev/urandom` すると印刷ダイアログが出ることがある

皆さんは /dev/urandom と呼ばれるUnixデバイスをご存知でしょうか. /dev/urandom は一言でいうと擬似乱数を出力する疑似デバイスで, catすると以下のようにランダムなバイト列を逐次的に出力してくれます. 出力するバイトの値域に特に制限は無いため, ターミナルに印字不可能な文字が表示されたりします.

良い具合にバイト列が揃うと漢字が流れてきたり, 異国の文字が流れてきたりと眺めているだけでも結構面白いです. 皆さんも是非お試し下さい. 私は絵文字が流れてきたのを見て大喜びしてました.

ところでこの cat /dev/urandom ですが, iTerm2でひたすら動かしていると稀に印刷ダイアログが開くことがあります.

f:id:mizdra:20191204011544j:plain
印刷するともれなく異国の文字たちがお出迎えしてくれます

実は cat /dev/urandom しなくても echo コマンドで簡単に再現できます. 試しにお手元のiTerm2で以下のコマンドを実行してみて下さい.

echo '\e[i'

印刷ダイアログは出ましたか? 出た方は記念に1枚プリントしておきましょう (しなくても良いです). 印刷はともかくとして, \e[i という文字列, これは一体何なのでしょうか.

ANSI Escape Sequence

ターミナルにはANSI Escape Sequenceと呼ばれる一部の文字列をターミナルを制御する特別な文字列として扱う機能が存在し, これを用いることでプログラムからターミナルを制御できるようになっています. 代表的なANSI Escape Sequenceには文字色の変更, スクリーンの消去, カーソルの移動などがあります. vimやemacsでカーソルを移動できたり, シンタックスハイライトが効いたりするのはこうした文字列のおかげなんですね.

もちろん \e[i もそうした文字列の1つで, 「表示されている画面を印刷する」よう指示する文字列として機能します. また \e[i には, カーソルのある行を印刷するよう指示する \e[1i, 入力された文字列をプリンタへとechoするモードに切替える \e[5i, モードを元に戻す \e[4i などの亜種があります.

## Hello World が印刷される
echo '\e[5iHello World!\e[4i'

iTerm2における \e[i

元々ANSI Escape Sequenceは1978年に登場したビデオ端末「VT100」を操作するために開発された仕組みで, 現代の端末エミュレータの多くがこれをエミュレートするよう設計されています*1. iTerm2もその内の1つで, リポジトリを「VT100」で検索するとそれっぽいコードがヒットします.

実際に \e[i を処理しているコードを追ってみると, VT100CSIParser.m:673\e[ni (n は任意の数) をトークンへとパースし, VT100Terminal.m:1791でトークンと n を見て印刷ダイアログなどを呼び出しています. よくよく見るとiTerm2では \e[i \e[5i \e[4i の3つの文字列しかエミュレートしていないことが分かったりします.

端末エミュレータによって対応はまちまちのようで, MacOS標準のTerminal.appでは亜種どころか \e[i すらサポートされていませんでした. というか \e[i をサポートしているiTerm2以外の端末エミュレータはそもそも存在するのでしょうか(Macだとこの2つくらいしか動作確認できなかった). 皆さんからの情報提供をお待ちしています.

活用法

Meta+P が壊れて動かなかない時や, どうしても Meta+P を使いたくない時に活用できそうです (本当に?). ダイアログが出て邪魔という点に注目すると, CUIゲームに登場する敵キャラの妨害攻撃とかにも使えそうですね (本当に?). 真面目なケースだとターミナルブラウザにおける window.print() のエミュレートあたりでしょうか.

おまけ

ダイアログ出せると知ったら当然やりますよね.

ちゃんと対策されてました. 20へえ.

*1:https://ja.wikipedia.org/wiki/%E7%AB%AF%E6%9C%AB%E3%82%A8%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%BF より. 平成生まれの人間なので詳しくは知りません.