• 行(ライン)の「黄金」

コンピューターから見ると、人間の速度は非常に遅い。タイピングコンテストの優勝者で1秒間に16、7キー程度。平均的には1分間に100ワード前後といわれているので、1.7キー/秒程度である。しかし1秒あれば、クロック4GHzの8コアCPUは、320億(8×4×10の9乗)個の命令を実行できる。

また、人間の行動はコンピューターからみるとまったく予想がつかない。コマンドラインで「di」と打ったからといってすぐに「r」が来るとは限らない。なので、コマンドラインでは、ユーザーがエンターキーを打ってから入力を解析し、処理を始めるようになっている。GUIでもエンターキーが 「入力の終了」=「処理の開始」を意味することは少なくない。エンターキーとは「入力終わりましたよ」というヒトからコンピュータに送る信号なのだ。コンピューター(プログラム)から見れば、キー入力で行末にエンターキーが押されるかどうかは大きく違う。

しかし、コンピュータを使っていると、エンターキーのない入力が必要になることがある。たとえば、表示などを一時停止して人間に読む時間を与えるような場合だ。「何かキーを押してください」というヤツである。あるいはゲームプログラムなどで、キーボードを使って操作をするような場合もある。今回は、こうしたエンターキーが不要の入力方法を考えてみる。

エンターキーなしの入力が必要な場面に、エスケープシーケンスによるカーソル位置を問いあわせがある。多くのターミナルエミュレーターでは、エスケープシーケンスで画面制御ができるが、状態の取得もエスケープシーケンスで行える。このとき、応答はキー入力としてやってくる。通常はプログラムの中で行われるので目にすることはないと思うが、エンターキーなしの入力を簡単に実行する「例」としてこれを使う。

カーソル位置の問いあわせ“Esc [ 6 n”というエスケープシーケンスを「表示」すると、“Esc [ 〈X〉; 〈Y〉R”というキー入力(応答)が返る。これを読めばいいのだが、行末記号がなく、キー入力でいえば、エンターキーを押していない状態になる。これを読むのには、ちょっと工夫がいる。

Linuxのbashには、readという組み込み関数があり、これで入力が可能になる(詳細はman readを参照のこと)。さすがはLinuxである。Unix時代のsh、後継のbashと長年、改良されてきたものなので、さまざまな使い方に対応している。readコマンドは、オプションの指定で行末文字を任意のものに指定できる。カーソルの位置取得は、最後が必ず大文字の“R”で終わるので、これを行末記号とすればよい。具体的には、


read -s -d 'R' response

とすれば、Rの直前までのキー入力をシェル変数responseに格納できる(写真01)。

  • 写真01: Linuxのbashではreadコマンドを行末文字の指定(-d 'R')や、タイムアウト処理(-t 0.05 -n 10)などで実行させることでエンターキーの必要ない文字入力が可能。行末記号を指定する方法では、末尾のRは入力から取り除かれるため1文字少ない。16進数表示の末尾に0aがあるのは、readコマンドが文字列をシェル変数に格納するためについたもの

もう1つの方法として、タイムアウトを使う方法がある。readコマンドは、入力待ちする時間を“-t”オプションで設定でき、入力文字数を“-n”で指定できる。以下のコマンドで最大10文字を0.05秒以内に入力することができる。


read -s -t 0.05 -n 10 response

最大10文字は、応答のエスケープシーケンスで〈X〉、〈Y〉ともに3桁の場合だが、実際にはこれ以下の文字数になる場合がある。そのときには、-tで指定した0.05秒でタイムアウトしてreadコマンドの実行が終わる。このときreadコマンドはエラーにはなるがresponseには入力した文字が入る。

Windows PowerShellはキー入力コマンドとしては、エンターキーを必要とする行入力を行うRead-Hostコマンドしかない。なので、キーからの1文字入力には、.NET FrameworkのSystem.Consoleクラスのメソッド“ReadKey()”を使う。具体的には、


$k=[Console]::ReadKey()

などとすれば入力キー1つを取得できる。末尾が「R」になる文字列を待つには、


$x="";do { $t=[Console]::ReadKey(); $x+=$t.keychar } while ($t.keychar -ne 'R')

などとする(写真02)。

  • 写真02: Windows PowerShellでは、.NET FrameworkのConsoleクラスにあるReadKeyメソッドを使い、'R'を受信するまでdo~whileで繰り返す。かなりプログラム的発想が必要で結構面倒

今回のタイトルの元ネタは、ワーグナーのオペラ「ラインの黄金」である。上演に四日間かかり、長いことでも有名な「ニーベルングの指輪」の最初の部分である。ライン川の底で見つかった黄金が「世界を支配する」指輪になる。コンピュータにとってエンターキーが行(ライン)で「黄金」にも匹敵する重要さを持つことからこの作品をネタにした。この指輪を巡って神々や巨人、ニーベルング族が争う。終段で演奏される「ヴァルハラ城への神々の入城」では、黄金を奪われた水底に住む「ラインの乙女」たちが、地上では虚偽と卑劣が栄華を誇ると嘆く。この曲は映画「エイリアン:コヴェナント」(2017)でも使われている。