rcmdnk's blog
Last update

GNU Screen: The virtual terminal manager

前回に引き続き 次はscreen -Xでコマンドを送るときのついての話。

msgminwaitを0に

msgminwaitはステータスラインにメッセージを送る様なコマンドを一度送り、 次のコマンドが来て、それもステータスラインにメッセージを送るコマンドだった場合、 直ぐに次のコマンドに行くのではなくmsgminwait秒だけ待ってくれる、 という時間を設定します 1

man screen:

   msgminwait sec

   Defines the time screen delays a new message when one message  is  cur-
   rently displayed.  The default is 1 second.

通常のscreen内のコマンドであれば一つ一つ実行していくだけで問題が無いようなんですが、 screen -Xを使ったスクリプトをキーバインドで呼ぶようなときには 問題が起こるようです。

msgminwaitが0でない状態で(デフォルトは1)screen -Xを大量に含む スクリプトを呼ぶと(特に領域を分割したりするちょっと時間がかかるような場合(?))

  /tmp/uscreens/S-USER/xxxxx.ttys002.yyy: connect: Connection refused

screen -Qのところでも出てきた エラーが出ます(もしかしたらこの部分はmsgminwaitの問題かも)。

これをmsgminwaitを0に設定すると消えてくれました。

なので、とりあえずmsgminwait.screenrc内で

msgminwait 0

しておいた方が良さそうです(下に書いてあるように何故か上手く機能しないので いずれにしろ0で無い意味がないので)。

execをキーバインド内に入れる場合

次の様なキーバインドを考えます。

bind a eval 'exec /bin/sh -c "~/set_win.sh"' 'split' 'focus' 'select 1'

やってることは

  • set_win.shを実行(ファイルは$HOME/へ設置)
  • 画面を横分割
  • 次の領域に移る
  • $win番のウィンドウを表示

ここで、set_win.shの中身は

~/set_win.sh
1
2
#!/bin/sh
screen

と、screenコマンドで新しいウィンドウを作るだけのコマンド 2

もともとウィンドウ0だけがあって、これによってウィンドウ1ができる状態を考えます。

それからCtrl-a aとして上のキーバインドを実行。

そうすると、2つ目の領域がWindow 1になって欲しいのですが そうなりません。

画面は分割されてWindow 1は作られて、さらにフォーカスも 2番めの領域に行っていますが、2番めの領域は空のママ。

ただ、新しくウィンドウを作るコマンドも、分割コマンドなども実行はされているようなので、

試しに、もともと、ウィンドウ0,1が両方ある状態で、上のコマンドを実行すると、 2個めの領域に正しくウィンドウ1が表示されました。 (この時、もちろん、ウィンドウ2が新たに出来ています。)

どうも、キーバインドでexecを使ってスクリプトを呼ぶと、 その操作の終了を正しく待たずに次へ行ってしまう様です。

なので、最初の場合、screenコマンドが終わる前にselect仕様として無い、という状態。

これを、

bind a eval 'screen' 'split' 'focus' 'select 1'

の様にすべてscreenコマンドで収めると、 2つに分解され、上も下もWindow 1になります。

これは、最初のscreenコマンドで新しくWindowを作るときに新しいウィンドウ(Window 1) に移動し、そこでsplitして新たな領域に移動してそこでもWindow 1を出す、 ということをしてるので正しいです。 (execの中だとサブシェルになるので(?)screenしても新しいウィンドウを作るだけで そちらに移動はしないみたいです。)

逆に、

~/set_win.sh
1
2
3
4
5
#!/bin/sh
screen
screen -X split
screen -X focus
screen -X select 1

として、

bind a eval 'exec /bin/sh -c "~/set_win.sh"'

の様に、全部スクリプトの中だけで行うようにすると、 分割され、かつ、最初の領域にはウィンドウ1が、 次の領域にはウィンドウ2が来てそちらにフォーカスが、 という感じになり、正しく動いているように見えます。

これをさらに、

~/set_win.sh
1
2
#!/bin/sh
screen
~/set_win2.sh
1
2
3
4
#!/bin/sh
screen -X split
screen -X focus
screen -X select 1

と2つのスクリプトに分けて、

bind a eval 'exec /bin/sh -c "~/set_win.sh"' 'exec /bin/sh -c "~/set_win2.sh"'

と、順番に呼んであげると、

Filter running: ... /bin/sh -c "~/set_win.sh"

の様な表示がステータスラインに表れて、画面が分割されません。 set_win.shの方は実行されているようで、新しいウィンドウはできていますが、 set_win2.shの方は実行されてないようです。

この場合は、最初の例と同じように最初のスクリプトの終了を待たずに 2個めのスクリプトの実行にかかっているようですが、 その時に、中でscreen -Xを使うに、前のスクリプトでのアクセスが行われているために 拒否されて失敗した状態、何だと思います。

screen -X screen

上ではスクリプト内でscreenコマンドをうつことで新しいウィンドウを作っていますが、 これだと少しおかしな動作をします。

screenを起動した中でコマンドラインからscreenコマンドを打つと 新たにセッションが始まるのではなく、そのセッション内で新しいウインドウが出来るわけですが、 ちょっと使っていたらCtrl-h左一文字消す、 という動作から、左側全部消す(Ctrl-uみたいな動作)% になってしまいました。

この辺よくわからなかったんですが、 screenの代わりに、screen -X screenと、-Xで 中に直接screen内のscreenコマンドを実行(コチラは常に新しいウィンドウを作る) 問題なく使えました。

screen -X後の待ち時間

追記: 2014/05/27

screen -Qが使えないので、ウィンドウが存在しているかどうかについて、

screen -X register . test
screen -p 0 -Q writebuf ~/test_file
if [ -f ~/test_file ];then
   echo window 0 exists!
fi

みたいな事をして、screen -p 0 -Xを使い、 特定のウィンドウを指定してコマンドを送り、 存在していればwritebufによってレジストリの書き出しによりファイルを作成し、 存在しなければこのコマンドが実行されない、ということを使っています。

この時、MacやWindowsで使うときには問題なかったのですが、 一部のLinuxで上手く作用しない時がありました。

どうやら、screen -Xコマンドが実行されてる間にファイルを先に チェックしに行ってしまってファイルが出来てない、と勘違いしてしまうようです。 (screen -Xはscreen内にコマンドを送るだけで、中のコマンドが終了するしないにかかわらず、 シェルスクリプトの中ではコマンドを送った段階で次に行ってしまう様。)

screen -p...の後に、sleep 1を入れるときちんと動きましたが、 1秒待つのはだるいので 3、 コマンドを待つのをチェックするために

screen -X register . test
screen -p 0 -Q writebuf ~/test_file
while [ 1 ];do
  if ! ps -u$USER |grep "screen -p $i -X writebuf $test_file";then
    break
  fi
done
if [ -f ~/test_file ];then
   echo window 0 exists!
fi

こんな感じでやってみたらうまくいきました。 ただ、上のループの中にechoとか入れてチェックしてみると、 このループは毎回1回しか呼ばれてないし、 よく考えてみると、screen -X自体のコマンドは終わってるはずなので psで引っかかるわけがありません。

ただ、上手く行ってるので、試しに、

screen -p 0 -Q writebuf ~/test_file
ps >/dev/null
if [ -f ~/test_file ];then

みたいにしても上手く行きました。 つまり、単にpsにちょっと時間がかかってその分で十分な待ち時間が出来ただけのようです。

usleepが無い場合もあるので、 ちょっとあれですが適当なコマンドで、ということで、最終的には 状態の依存性が少なそうな

screen -p 0 -Q writebuf ~/test_file
date >/dev/null
if [ -f ~/test_file ];then

dateを使うことに。これで今のところ使ってる環境では全部大丈夫で、 それほど遅延も気になりません。

ちなみに、echoも使ってみましたがこちらだと全く遅延を産まないのか 入れない場合と変わりませんでした。

追記ここまで

まとめ

screen -Xを使う様なスクリプトをキーバインドに割り当てたい場合は

  • msgminwait 0にしておく。
    • どうしても普段は0にしたくないならスクリプトの最初と最後に screen -X msgminwait 0screen -X msgminwait 1(もしくは設定したい時間) とかにすれば良いかも。
  • キーバインドにはスクリプト一つだけを設定し、一つのキーバインドで複数のスクリプトは実行しない。
  • スクリプト内でscreen単体は使わない。新しいウィンドウを作りたいときはscreen -X screenをする。

ということで。

Sponsored Links
  1. ただ、今ちょっと試してみたらなんか上手く設定できないみたいで msgminwait 5と設定しても(msgwait 5も設定)

    bind a eval 'echo aaa' 'echo bbb'
    

    みたいなことしても直ぐにbbbが表示されるし

    bind b eval 'echo ccc' 'echo ddd'
    

    として、aを実行して5秒以内にbを実行してもすぐdddが表示されます。

    何か変わったのかscreenのバグなのか何か自分の設定がおかしいのか。 前に色々設定をいじろうとした時に試してみた時は きちんと働いてた様な気もするのですが。。。

    ただし、上に書いてあることに関してはこのような状況でも 0かそうでないかで影響はありました。

  2. ちなみに後の方に書いてあります、スクリプト内でscreenだけを使って新しい ウィンドウを作ると少し変な動作をします。 中で、screen -X screenとすると、screenの中での新規ウィンドウを作る、 こまんどなscreenになるのでそちらを使ったほうが良さそう。 ただ、ここでの話はどちらで書いても全く一緒です。

  3. usleepコマンド(micro秒sleep)があればusleep 1でも大丈夫でした

Sponsored Links

« GNU screenでscreen内の情報をシェルスクリプトで取り出す & 問題点 Macで入力ソースにいつの間にかU.S.が入ってきて消せない時の対処法 »

}