純規の暇人趣味ブログ

首を突っ込んで足を洗う

[Bash]screenが終了するまで待機する

      2016/03/18    HimaJyun

僕の運営するマイクラ鯖はscreenで動かしています。
そして、1日1回、再起動とバックアップが実施されています。

しかし、この過程でscreenで実行されているマイクラ鯖が終了されるのを待つ必要があったため、waitコマンドの様なscreenの終了待ちを考えてみました

screenの終了待ち

例えばですが、もし今あなたがBashスクリプトを組んでいて、その中で時間が掛かる処理をバックグラウンドで実行させたいとするでしょう。
もしそうであれば、コマンドの後ろに「&」を付けてバックグラウンド化し、必要であれば「wait」で待機すれば良いです。

しかし、それが通用しない場合があります。
そうです、Minecraftの様な、ソフトウェア自身がCUI(?)を持ち、起動してしまうとそいつがキーをバインドしてしまう場合です。

キーをバインドされちゃうとwaitもクソもありませんし、そう言ったソフトウェアは終了に専用のコマンドを送りつけたりする必要があります。(Minecraftであれば「stop」)

そのためにはscreenを使って、仮想端末を作成するのですが、最初に説明した通り終了が待機出来ません。
しかし、ちょっとした応用技を使うだけで簡単に出来てしまいます。

前提条件

後から紹介する手法は、起動しているscreenから対象のプログラムが識別出来る必要があります。
ですので、起動時に以下の様にして起動して下さい。

screen -AmdS [一意な名前] 実行するコマンド

こうする事で、「screen -list」した時に名前が付きます。
アタッチも「screen -r [名前]」で出来ますし、便利ですよ。

終了待ち

実際の説明は後回しにするとして、以下の様なコードで実現可能です。

function screen_wait() {
  local sleep_time=${2:-1}
  while [ -n "$(screen -list | grep -o "${1}")" ]
  do
   sleep ${sleep_time}
  done
}

「screen_wait [screenの識別名] [停止確認間隔(省略可能)]」のように呼び出して下さい。

例えばですが、「screen -AmdS hoge」で起こしたscreenの終了を待つのは「screen_wait hoge」です。

原理

シェル慣れしている人に見せれば「あぁ、なるほど!!」ですが、分からない方は本当に分からないでしょうから軽くご説明致します。

「screen -list」を実行すると起動しているscreenのリストが出ます。
これを「|」(パイプ)でgrepに送り、${1}(第一引数=screenの識別名)で抽出(-o)しています。

それらの処理結果を判定し、空文字以外(-n)ならtrueとし、whileの中に入ります。

whileループは中にsleepがあるだけですね、無くても良いですけど、無ければ無いでループが高速で回って余計な負荷になるので入れています。
(どうでも良いですけど、while(ホワイル)+ループなのでホワイループとか勝手に呼んでます。)

一連の処理が終わると、また最初に戻って「screen -list | grep -o ${1}」して判定します。

要は、終了待ちしているscreenが無くなるまでループで回って監視する、と言う古典的な手法です。
流れてる処理の中ででも、処理待ちが、ほら、ぐるぐる回って……ですね。

使い道

僕はこの手法をマイクラ鯖のバックアップ&再起動スクリプトに使用しています。
マイクラ鯖が確実に終了してからバックアップを開始するためにね。

例えばですが、マイクラ鯖は良くscreenを使用して以下の様に起動します。

screen -AmdS minecraft java -jar server.jar nogui

「minecraft」と言う識別名で仮想端末を作成し、その中で「java -jar server.jar nogui」を実行する、良くあるマイクラ鯖の起動スクリプトですね。

そのマイクラ鯖を自動で終了するのは、ざっと説明すると以下の様なコマンドを実行します。

screen -p 0 -S minecraft -X eval 'stuff "stop\015"'

(マイクラ鯖の自動再起動に関するより詳しい情報をお求めであれば「Minecraftサーバをscreenとcronでプラグインを使わずに自動再起動する」をご覧下さい。)

しかし、screenは何度も言っている様に「バックグラウンド」で実行されています。
先程のコマンドはマイクラ鯖のCUIに「stop」と言う文字を送ったに過ぎません

このままバックアップを開始すると、データが正しく保存されていないにも関わらず処理が開始されてしまいます。

そこで先程の関数の出番です、「screen_wait minecraft」で呼び出しましょう。
そうすると、screenが終了(=マイクラ鯖)が終了するまでループで待ち続けます。

このループを抜ければscreenが終了した、すなわちマイクラ鯖が終了したと言う事ですので安心してバックアップを開始出来ます。

こうする事でバックアップや再起動の処理をより確実にしています。

 - サーバ運営 ,