GNU Screen のちょっと便利な使い方

今日はかなり珍しく技術的な話題を提供します。

要約
[困ったこと]

  • 1つの Screen で複数のサーバにたくさん telnet するとどの仮想端末で何をしていたか忘れる!
  • 選択が面倒!

[この記事を読むとできること]

  • Screen のウィンドウタイトルにユーザ名、ホスト名、作業中のディレクトリを表示
  • どのサーバにどのウィンドウでログインしているかを確認しながらウィンドウ選択


知っている方もかなり多いのかもしれませんが、bash には PROMPT_COMMAND という環境変数があります。PROMPT_COMMAND を使うことで、PROMPT が表示される時に自動的に実行するコマンドを指定できます。

これにたとえば私の場合ですと、次のように設定しています。

 PROMPT_COMMAND='echo -ne "\033]0;$(whoami)@$(hostname):$(pwd)\007"'

上記は、エスケープシーケンスを含む出力で、慣れた人にとっては、すぐに理解できると思いますが、初めて見る人はぎょっとするかもしれません。

簡単に言うと、ターミナルのタイトルバーに表示されるタイトルを
「$(whoami)@$(hostname):$(pwd)」に変更するということを意味しています。

bash では、$() で囲むとその囲んだ文字列をコマンドとして実行して、その結果の標準出力に $() で囲まれた部分で置き換えられます。つまり、ユーザが cuzic のときに $(whoami) を実行すると、cuzic という文字列に置き換えられます。同様に $(hostname) はその実行している環境のホスト名に、置き換えられ、$(pwd) は実行時のディレクトリに置き換えられます。

ユーザが cuzic ホスト名が your-host でディレクトリが /home/cuzic/ であれば実行結果は、

cuzic@your-host:/home/cuzic/ 

になります。

つまり、

 PROMPT_COMMAND='echo -ne "\033]0;$(whoami)@$(hostname):$(pwd)\007"'

環境変数の設定を行うことで、ユーザ名、ホスト名、現在のディレクトリがタイトルバーに表示されます。

タイトルバーの表示内容は Windows 等の環境で一般的にアイコン化しているときに表示されますので、ターミナルをたくさん同時に開いているときにどのサーバにログオンしているかについてすぐに分かって、便利です。

tcsh をお使いの場合は precmd という名前で alias コマンドを作れば同様のことを実現可能です。

さて、ここまでは前置き。

複数の端末を別のウィンドウで同時に開くときに、この手法は便利ですが、そうではなく複数の TTY セッションを1つのウィンドウで開きたい場合もあります。

強引な展開を気にしないことにして、複数の TTY セッションを1つのウィンドウで開くために GNU Screen と呼ばれるとても有名なアプリケーションがあります。GNU Screen を使うと、複数の TTY セッションを同時に開くことができるだけでなく突然回線断が生じてしまった場合などでもサーバにその Screen セッションに再接続すれば、作業途中の TTY セッションに復帰できるため、大変便利です。

さて、どれくらい知られている機能かどうか分かりませんが、Screen で開いている TTY セッションのタイトルを指定するということも可能です。先ほどの PROMPT_COMMAND と同様の文字列を指定する場合は次のコマンドを実行します。

 echo -n -e "\033k$(whoami)@$(hostname):$(pwd)\033\134

これで、Screen によって管理されるタイトル文字列を変更できます。

screen の設定ファイル、.screenrc では screen を起動するときに環境変数を指定する機能があります。その機能を使って

 setenv PROMPT_COMMAND 'echo -n -e "\033k$(whoami)@$(hostname):$(pwd)\033\134"'

とすることで、screen によって管理される TTY セッションでは、ウィンドウタイトルバーではなく、Screen によって管理されるタイトル文字列を変更できます。

Screen にはタイトル文字列を見ながら使用する TTY セッションを選択する機能として「windowlist -b」 コマンドがあります。このコマンドを使用することで、何番目の TTY セッションで何をしていたか、といったことを覚えていなくても、簡単に TTY セッションを選択できるようになります。

さて、けれども人間の欲望というのは限りないものです。

多くの Screen を使う用途では、1つの Screen でさまざまなホストに対してログインしていることでしょう。Web の開発サーバと本番サーバに同時にログインするといった状況です。このさまざまにログインしているサーバでどれにログインしているかについて、簡単にタイトルバーの文字列や、GNU Screen のタイトル文字列で簡単に確認したいですよね。しかも、直接ログインした場合と Screen 経由でログインした場合の両方で対応できるようにしたいものです。

これは、一見不可能な要求のように思います。
直接ログインしているか、Screen 経由でログインしているかを判別するためにログインする元のホストからサーバに対してなんらかの情報を送る必要があるからです。

しかし、これは可能です。
例えば Telnet にはクライアントの環境変数を送る方法が用意されています。

environ コマンドです。environ コマンドを使うことで、新たな環境変数を定義したり、クライアント側の環境変数をエキスポートすることができます。

Telnet のプロンプトを利用すればインタラクティブに設定できるのですが、今回はより簡単な方法として、telnet の環境設定ファイルである .telnetrc に設定する方法を紹介します。

まず下準備として、.screenrc に次の設定を追加します。

 setenv SCREEN true

この例では setenv コマンドを用いることで、環境変数 SCREEN に true という文字列を設定しています。

次に、.telnetrc に次の設定を追加します。

 dev-host
   environ export SCREEN
 homban-host1
   environ export SCREEN
 homban-host2
   environ export SCREEN


上記の設定を行うことで、dev-host, homban-host1, homban-host2 に telnet するときに、クライアントの環境変数 SCREEN の情報を送ることができます。

環境変数 SCREEN は GNU Screen を経由せずに telnet する場合は設定されていませんので、サーバ側での 環境変数 SCREEN は設定されません。一方、GNU Screen を経由して、telnet する場合は、.screenrc の設定に従い、環境変数 SCREEN は設定されているため、その設定がそのままサーバ側に送られています。

ここまでくれば後は簡単ですね。

環境変数 SCREEN の設定に従って、サーバ側の初期化ファイルに分岐を書くだけです。

例として サーバ側のログインシェルを tcsh と仮定すると、次のような設定を.cshrc に記述することになります。

 if ( $?SCREEN ) then
  alias precmd 'printf "\033]0;`whoami`@`hostname`:`pwd`\007"'
 else
  alias precmd 'printf "\033k`whoami`@`hostname`:`pwd`\033\134"''
 endif

この場合は、別の例を示すために、echo ではなくprintf(1)コマンドを用いています。そして、bash にある $() で囲まれた範囲を置き換える機能は tcsh にはありませんから代わりにバッククオート(`)を用いています。

このような設定を行うことで、環境変数が SCREEN が設定されているか否か、つまり SCREEN 経由かどうかに従って、タイトルバーか GNU Screen のどちらのタイトル文字列を変更するかを選択できます。

これで、GNU Screen の windowlist -b コマンドによって、簡単にどの TTY セッションを用いるかを選択できます。
windowlist -b コマンドは多用するので、.screenrc に次の設定を書いています。

 bind ' ' windowlist -b 

これで、「(prefix): windowlist -b」 と入力するのではなく、「(prefix) [スペース]」と入力することで、「windowlist -b 」を実行できます。(prefix) が ^t に設定されていれば、「コントロール+T、スペース」と押すことになります。

なお、補足ですがサーバにログインするのに telnet ではなく、ssh を用いる場合でもクライアント側の環境変数を送ることはできます。ssh_config(5) を見ると分かりますが、SendEnv という設定をクライアント側に書いて、サーバ側に AcceptEnv を書くことで実現できます。詳しくはマニュアルページをご参照ください。

以上 GNU Screen を使った簡単な Tips 紹介でした。