UNIXで任意のスクリプトをデーモン化するにはどうすればよいですか?


93

私は、任意に、汎用スクリプトやコマンドを有効にすることができますdaemonizerたいデーモンを

対処したい2つの一般的なケースがあります。

  1. 永久に実行する必要があるスクリプトがあります。停止した場合(または再起動時)、再起動します。同時に2つのコピーが実行されないようにしてください(コピーが既に実行されているかどうかを検出し、その場合は起動しないでください)。

  2. 単純なスクリプトまたはコマンドラインコマンドがあり、それを永久に繰り返し実行し続けたいと思っています(実行と実行の間に少し間を置いて)。この場合も、スクリプトの2つのコピーが同時に実行されないようにしてください。

もちろん、ケース2のスクリプトの周りに「while(true)」ループを記述して、ケース1のソリューションを適用するのは簡単ですが、より一般的なソリューションは、ケース1のスクリプトに適用されるため、ケース2を直接解決するだけです。ウェル(スクリプトがスクリプトが本当にあれば、これまで当然のことながら(死ぬために意図されていない場合、あなただけの短い、あるいはまったく一時停止をすることができ、一時停止が実際に問題ではない、その後死ぬことはありません))。

ソリューションには、たとえば、既存のスクリプトにファイルロックコードやPID記録を追加する必要がないことに注意してください。

より具体的には、次のように実行できるプログラム「デーモン化」が欲しい

% daemonize myscript arg1 arg2

または、例えば、

% daemonize 'echo `date` >> /tmp/times.txt'

これにより、times.txtに追加される日付のリストが増え続けます。(daemonizeの引数が上記のケース1のように永久に実行されるスクリプトである場合、daemonizeは引き続き正しく機能し、必要に応じて再起動します。)次に、上記のようなコマンドを.loginに追加できます。および/またはそれを1時間ごとまたは1分ごとにcronする(私がそれが予期せず死ぬことについてどれほど心配していたかによる)。

注意:デーモン化スクリプトは、デーモン化しているコマンド文字列を覚えておく必要があります。これにより、同じコマンド文字列が再度デーモン化されても、2番目のコピーは起動されません。

また、このソリューションはOS XとLinuxの両方で理想的に機能するはずですが、どちらか一方のソリューションは歓迎されます。

編集:で呼び出す必要がある場合は問題ありませんsudo daemonize myscript myargs

(私がこれをすべて間違っていると思っている場合、または迅速で汚い部分的な解決策がある場合は、それも聞きたいです。)


PS:それは便利だ場合は、ここにあるのpythonに似た質問特定。

そして、同様の質問に対するこの回答には、任意のスクリプトを素早く汚い悪魔化するための有用なイディオムがあるようです。


1
参照してくださいserverfault.com/questions/311593/...純粋なシェルバージョン
w00t

回答:


90

nohupと&演算子を使用して、Unixで任意の実行可能ファイルをデーモン化できます。

nohup yourScript.sh script args&

nohupコマンドを使用すると、スクリプトを強制終了せずにシェルセッションをシャットダウンできます。一方、&はスクリプトをバックグラウンドに配置して、セッションを続行するためのシェルプロンプトを表示します。これに関する唯一の小さな問題は、標準出力と標準エラーの両方が./nohup.outに送信されることです。そのため、このマナーで複数のスクリプトを開始すると、それらの出力が絡み合います。より良いコマンドは:

nohup yourScript.sh script args >script.out 2>script.error&

これにより、標準出力が選択したファイルに送信され、標準エラーが選択した別のファイルに送信されます。標準出力と標準エラーの両方に1つのファイルのみを使用する場合は、次のようにできます。

nohup yourScript.sh script args >script.out 2>&1 &

2>&1は、標準エラー(ファイル記述子2)を標準出力(ファイル記述子1)と同じファイルにリダイレクトするようにシェルに指示します。

コマンドを1回だけ実行し、コマンドが停止した場合に再起動するには、次のスクリプトを使用できます。

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

最初の引数は、使用するpidファイルの名前です。2番目の引数はコマンドです。そして、他のすべての引数はコマンドの引数です。

このスクリプトにrestart.shという名前を付けると、次のようになります。

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &

驚くばかり; ありがとうございました。再起動を遅らせるオプションも必要なのでしょうか。それとも、より良いだけで、このと一緒にそれを使用するには: stackoverflow.com/questions/555116/...は
dreeves

4
これはSIGHUPのみを処理します。処理すべき他の(通常は)致命的な信号があります。
Tim Post

このスクリプトを改善するもう1つの方法は、$ PIDFILEを引数として指定することを要求するのではなく、独自に$ PIDFILEを配置するための適切な場所を作成することです。後片付けさえしません!(これはaで簡単なはずですtrap EXIT
Steven Lu

また、使用することを考える<では、testASCIIの比較ではない整数比較です。それでも動作する可能性がありますが、バグが発生する可能性があります。
Steven Lu

このスクリプトの修正をここに投稿しまし
Steven Lu

33

長い回答をお詫び申し上げます(私の回答が仕様をどのように規定しているかについてのコメントをご覧ください)私は包括的であるように努力しているので、あなたは可能な限り上手に立ちます。:-)

プログラムをインストールでき(ルートアクセス権があり)、デーモン実行用にスクリプトを設定するために1回限りのレッグワークを実行する場合(つまり、コマンドライン引数を指定してコマンドラインで実行するよりも複雑な場合)ただし、サービスごとに1回実行するだけでよい)、より堅牢な方法があります。

これには、daemontoolsの使用が含まれます。投稿の残りの部分では、daemontoolsを使用してサービスを設定する方法について説明します。

初期設定

  1. How to install daemontoolsの指示に従ってください。一部のディストリビューション(Debian、Ubuntuなど)にはすでにパッケージが含まれているので、それを使用してください。
  2. というディレクトリを作成し/serviceます。インストーラーは既にこれを実行しているはずですが、確認するか、手動でインストールする場合のみです。この場所が気に入らない場合は、svscanbootスクリプトで変更できますが、ほとんどのdaemontoolsユーザーは慣れているため、使用/serviceしないと混乱します。
  3. Ubuntuを使用している場合やinit、標準を使用していない(つまり、を使用していない)別のディストリビューション/etc/inittabを使用している場合は、プリインストールさinittabれているものをベースとして使用して、svscanbootから呼び出されるようにする必要がありますinit。難しいことではありませんinitが、OSが使用する構成方法を知っておく必要があります。 svscanbootはを呼び出すスクリプトでsvscan、サービスを探すという主な作業を行います。それはから呼び出されるinitのでinit、何らかの理由で死んだ場合は再起動するように手配します。

サービスごとの設定

  1. 各サービスには、サービスに関するハウスキーピング情報を格納するサービスディレクトリが必要です。これらのサービスディレクトリを格納する場所を作成して、すべて1か所にまとめることもできます。通常はを使用します/var/lib/svscanが、新しい場所であれば問題ありません。
  2. 私は通常、スクリプトを使用してサービスディレクトリを設定し、手作業の多くの手間を省きます。例えば、

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"

    ここでsome-service-nameuserはサービスに付ける名前、はそのサービスloguserを実行するユーザー、ロガーはとして実行するユーザーです。(ロギングについては少しだけ説明します。)

  3. サービスはフォアグラウンドで実行する必要があります。プログラムがデフォルトでバックグラウンドであるが、それを無効にするオプションがある場合は、そうします。プログラムをバックグラウンドで無効にする方法がない場合は、を読んでくださいfghack。ただし、これにはトレードオフがありますsvc。つまり、を使用してプログラムを制御することはできなくなります。
  4. runスクリプトを編集して、意図したとおりに実行されていることを確認します。sleepサービスが頻繁に終了することが予想される場合は、上部に電話をかける必要がある場合があります。
  5. すべてが正しく設定さ/serviceれたら、サービスディレクトリを指すシンボリックリンクを作成します。(サービスディレクトリを内に直接配置しないでください/service。サービスsvscanのウォッチからの削除が困難になります。)

ロギング

  1. daemontoolsのロギング方法は、サービスがログメッセージを標準出力(またはで生成されたスクリプトを使用している場合は標準エラー)に書き込むことですmkservicesvscanログメッセージをログサービスに送信します。
  2. ロギングサービスは、標準入力からログメッセージを取得します。によって生成されたロギングサービススクリプトmkserviceは、log/mainディレクトリに自動ローテーションされたタイムスタンプ付きのログファイルを作成します。現在のログファイルはと呼ばれcurrentます。
  3. ロギングサービスは、メインサービスとは独立して開始および停止できます。
  4. ログファイルをパイプ処理するtai64nlocalと、タイムスタンプが人間が読める形式に変換されます。(TAI64Nは、ナノ秒カウントの64ビットアトミックタイムスタンプです。)

制御サービス

  1. svstatサービスのステータスを取得するために使用します。ロギングサービスは独立しており、独自のステータスがあることに注意してください。
  2. を使用してサービス(開始、停止、再起動など)を制御しますsvc。たとえば、サービスを再起動するにはsvc -t /service/some-service-name、;を使用します。-t「送信SIGTERM」を意味します。
  3. 利用可能なその他の信号には、-hSIGHUP)、-aSIGALRM)、-1SIGUSR1)、-2SIGUSR2)、-kSIGKILL)があります。
  4. サービスを停止するには、を使用します-ddownサービスディレクトリにという名前のファイルを作成して、起動時にサービスが自動的に開始されないようにすることもできます。
  5. サービスを開始するには、を使用します-u。以前にダウンしていない(または自動起動しないように設定している)場合を除き、これは必要ありません。
  6. スーパーバイザーに終了を依頼するには、-x;を使用します。通常は-d、サービスの終了にも使用されます。これは、サービスを削除できる通常の方法ですが、/service最初にサービスのリンクを解除する必要がありますsvscan。そうしないと、スーパーバイザが再起動します。また、ロギングサービス(mkservice -l)を使用してサービスを作成した場合はsvc -dx /var/lib/svscan/some-service-name/log、サービスディレクトリを削除する前に、ロギングスーパーバイザ(など)も必ず終了してください。

概要

長所:

  1. daemontoolsは、サービスを作成および管理するための完全な方法を提供します。私は自分のサーバーにそれを使用しており、強くお勧めします。
  2. そのログシステムは、サービスの自動再起動機能と同様に非常に堅牢です。
  3. それはあなたが書いた/調整したシェルスクリプトでサービスを開始するので、好きなようにサービスを調整できます。
  4. 強力なサービス制御ツール:ほとんどすべての信号をサービスに送信でき、サービスを確実に起動および停止できます。
  5. サービスはクリーンな実行環境が保証されています。サービスは、提供されているものと同じ環境、プロセス制限などで実行されinitます。

短所:

  1. 各サービスは少し設定が必要です。ありがたいことに、これはサービスごとに1回実行するだけで済みます。
  2. フォアグラウンドで実行するようにサービスを設定する必要があります。また、最良の結果を得るには、syslogやその他のファイルではなく、標準出力/標準エラーにログを記録するように設定する必要があります。
  3. daemontoolsのやり方に慣れていない場合、急な学習曲線。を使用してサービスを再起動する必要svcがあり、実行スクリプトを直接実行することはできません(その場合、サービスはスーパーバイザの制御下にないため)。
  4. 大量のハウスキーピングファイル、および多数のハウスキーピングプロセス。各サービスには独自のサービスディレクトリが必要であり、各サービスは1つのスーパーバイザプロセスを使用して、サービスが停止した場合にサービスを自動再起動します。(多くのサービスあるsupervise場合は、プロセステーブルに多くのプロセスが表示されます。)

全体として、daemontoolsはあなたのニーズに最適なシステムだと思います。私はそれを設定して維持する方法についての質問を歓迎します。


私の答えが仕様にどのように釘付けになるか:1.サービスを設定する必要があります。重複を設定しない限り(およびサービスがそれ自体のバックグラウンドでない限り)、重複は発生しません。2. supervise、スーパーバイザは、終了するサービスの再起動を処理します。再スター間で1秒待機します。それでも足りない場合は、サービス実行スクリプトの先頭でスリープ状態にします。
Chris Jester-Young

2a。supervise自体はによってサポートされているsvscanため、スーパーバイザが停止した場合は再起動されます。2b。svscanはに支えられてinitおり、svscan必要に応じて自動再起動します。2c。あなたの場合はinit何らかの理由で金型、あなたはとにかくねじ込みます。:-P
Chris Jester-Young

ハウスキーピングに関する他の質問に答えるために、daemontoolsシステムはPIDファイルを使用しません。古くなっている可能性があるためです。代わりに、すべてのプロセス情報は、特定のサービスをサポートするスーパーバイザによって保持されます。スーパーバイザは、ツールが気に入っsvstatて操作svcできるサービスディレクトリに一連のファイル(およびFIFO)を保持しています。
Chris Jester-Young

3
このような投稿は、SOやネット全体にもっとあるはずです。望ましい効果を達成する方法に関するレシピだけでなく、レシピを説明するのに手間がかかるレシピ。なぜこれを2回以上賛成できないのですか?:|
skytreader 14

12

やってみたいと思うかもしれませんstart-stop-daemon(8)/etc/init.d例については、Linuxディストリビューションのスクリプトを確認してください。起動されたプロセスをコマンドライン呼び出しまたはPIDファイルで見つけることができるため、スクリプトのウォッチドッグ以外のすべての要件に一致します。ただし、必要に応じてスクリプトを再起動する別のデーモンウォッチドッグスクリプトをいつでも開始できます。


Fedoraにはまだstart-stop-daemonがないので、それに依存するスクリプトは移植できません。参照:fedoraproject.org/wiki/Features/start-stop-daemon
Bengt

OSXユーザー向けの注意事項:start-stop-daemonそこにもありません(10.9現在)。
mklement0 2013

@ mklement0ええと...ほぼ5年で多くの変化がありました。
Alex B

私、時間が飛んでくる。start-stop-daemonただし、Linuxはまだ生きており、キックしています。しかし、回答を読んだ後、stackoverflow.com / a / 525406/45375は、OSXが独自のことをしていることに気付きましたlaunchd
mklement0 2013

12

あなたはdaemonizeを見る必要があります。2番目のコピーを検出できます(ただし、ファイルロックメカニズムを使用します)。また、異なるUNIXおよびLinuxディストリビューションでも動作します。

アプリケーションをデーモンとして自動的に起動する必要がある場合は、適切なinit-scriptを作成する必要があります。

次のテンプレートを使用できます。

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0

1
正解の候補のようです。特に「シングルインスタンスチェック」を考慮します。
マーティンウィックマン

これは最良の答えかもしれません-よくわかりません-でも、もしそうだと思うなら、質問で私が挙げた仕様が間違っている理由についての説明を含めることもできますか?
dreeves

私はしませんようにkillproc停止する一部:あなたが言う、RANは、そのプロセスがあった場合javakillproc他のすべてのJavaプロセスがあまりにも殺されることになりますが。
Chris Jester-Young

1
/etc/rc.d/init.d/functionsからは、デーモンはちょうど新しいシェルからバイナリを起動します:$ -c素敵/ binに/ bashのを$のcgroup $corelimit >/dev/null 2>&1 ; $*だから私...何かをデーモン化するために起こっているのを疑う
mbonnin

1
私はこれが古いことを知っていますが、後でこれを見つける人にとっては...これは正しいです。/etc/init.d/functionsで定義されている「デーモン」は実際にはデーモン化しません。これは、cgroupsを実行し、プロセスがすでに実行されているかどうかを確認し、ユーザーを設定し、nice値とulimit値を設定するなどのラッパーにすぎませ。プロセスをデーモン化しませ。それはまだあなた自身の仕事です。:)
jakem

7

すでに述べたようにする代わりにdaemonizeしてdaemontools、そこにあるデーモン libslackパッケージのコマンドが。

daemon 非常に構成可能であり、自動再起動、ロギング、pidfile処理などの面倒なデーモンのすべてに注意を払っています。


5

特にOS Xを使用している場合は、launchdの動作を確認することをお勧めします。スクリプトが実行されていることを自動的にチェックし、必要に応じて再起動します。また、あらゆる種類のスケジューリング機能なども含まれています。要件1と2の両方を満たす必要があります。

スクリプトのコピーを1つだけ実行できるようにするためには、PIDファイルを使用する必要があります。一般に、現在実行中のインスタンスのPIDを含むファイルを/var/run/.pidに書き込みます。プログラムの実行時にファイルが存在する場合は、ファイル内のPIDが実際に実行されているかどうかが確認されます(プログラムがクラッシュしたか、PIDファイルを削除し忘れた可能性があります)。ある場合は中止します。そうでない場合は、実行を開始し、PIDファイルを上書きします。


5

Daemontools(http://cr.yp.to/daemontools.html)は、これを行うために使用されるかなりハードコアなユーティリティのセットで、dj bernsteinによって書かれました。私はこれをいくつかの成功で使用しました。それについての厄介な部分は、スクリプトを実行したときに、どのスクリプトも目に見える結果を返さないことです-目に見えない戻りコードだけです。しかし、いったん実行されると、それは防弾です。


はい、daemontoolsを使用するエントリも作成しました。私は自分の投稿を書くつもりです。私の答えをもっと包括的にしたいと思っており、その方法で報奨金を獲得したいと思っているからです。わかります。:-)
Chris Jester-Young

3

まずhttp://code.activestate.com/recipes/278731/createDaemon()から取得します

次にメインコード:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)

ああ、ありがとう!「daemonize foo arg1 arg2」と「daemonize 'foo arg1 arg2'」を実行できるように、少し一般的にしたいですか?
dreeves 2009

さて、これで引数が結合されます-ただし、引数内にスペースを入れたい場合は、変更する必要があります。
ダグラスリーダー09

ダグラス、ありがとう!ただし、大きな欠陥があります。「daemonize foo」を2回実行すると、fooの2つのコピーが実行されます。
dreeves

PID記録コードを追加することもできますが、スクリプトを1回だけ実行するのが最善かもしれません...
ダグラスリーダー

それは、「デーモン化」ラッパーのコンセプト全体の基本と考えています。(たとえば、1時間ごとまたは1分ごとにcronを実行して、常に実行されていることを確認できます。)これは間違っていると思いますか?createDaemonはすでに何らかの方法でそれを保証していますか?再起動後はどうですか?
dreeves

1

これは、空のディレクトリにコピーして試すことができる例を備えた作業バージョンです(CPANの依存関係をインストールした後、Getopt :: LongFile :: SpecFile :: Pid、およびIPC :: System: :シンプル -すべてかなり標準的で、すべてのハッカーに強く推奨されます:)で一度にすべてをインストールできますcpan <modulename> <modulename> ...


keepAlive.pl:

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

example.pl:

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

これで、上記の例を次のように呼び出すことができ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3ます。ファイルpidfileが作成され、出力が表示されます。

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3

私が正しく理解していれば、これは仕様にはまったく当てはまらないと思います。ソリューションでは(感謝、btw!)、デーモン化するプログラムを変更して、PIDをPIDファイルに書き込む必要があります。任意のスクリプトをデーモン化できるユーティリティを期待しています。
dreeves

@dreeves:はい、しかしそれを回避する方法は2つあります。1。keepAlive.plによって呼び出されたスクリプト(example.plなど)は、実際のプログラムを実行するための単純なラッパーであるか、2。keepAlive.plがアクティブなシステムプロセス(CPANのProc :: ProcessTableを使用)を使用して、関連するプロセスとそのpidを見つけようとします)。
Ether

1

Monitを試すこともできます。Monitは、他のサービスを監視および報告するサービスです。これは主に実行時の問題を(電子メールとSMSを介して)通知する方法として使用されますが、他の提案のほとんどが提唱していることも実行できます。プログラムの自動(再)起動および停止、電子メールの送信、他のスクリプトの開始、および取得可能な出力のログの維持を行うことができます。さらに、確かなドキュメントがあるので、インストールとメンテナンスが簡単であることがわかりました。


1

あなたは不滅を試してみることができます* nixクロスプラットフォーム(OSに依存しない)スーパーバイザーです。

macOSで簡単に試すには:

brew install immortal

ポートから、またはpkg を使用してFreeBSDを使用している場合:

pkg install immortal

Linuxの場合、プリコンパイルされたバイナリをダウンロードするか、ソースから:https : //immortal.run/source/

次のように使用できます。

immortal -l /var/log/date.log date

または、より多くのオプションを提供する構成YAMLファイルによって、例えば:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

標準エラー出力も別のファイルに保存したい場合は、次のようなものを使用できます。

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
stderr:
    file: /var/log/date-error.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

0

私は他の答えに一連の改善を加えました。

  1. このスクリプトからのstdoutは、コマンドがすでに実行されていることを検出したために終了しない限り、子からのstdoutだけで構成されます
  2. 終了時にpidfileの後にクリーンアップします
  3. オプションの構成可能なタイムアウト期間(正の数値引数を受け入れ、に送信sleep
  4. 使用プロンプト -h
  5. 単一のコマンド実行ではなく、任意のコマンド実行。最後の引数OR残りの引数(最後の引数が複数ある場合)がに送信されるためeval、デーモン化するための最後の引数(または末尾の引数)としてこのスクリプトに送信する文字列としてあらゆる種類のシェルスクリプトを構築できます。
  6. -lt代わりに行われる引数カウント比較<

スクリプトは次のとおりです。

#!/bin/sh

# this script builds a mini-daemon, which isn't a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.

if [ "$1" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=$1
shift

TIMEOUT=1
if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=$1
        [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
        shift
fi

echo "Checking pid in file ${PIDFILE}." >&2

#Check to see if process running.
if [ -f "$PIDFILE" ]; then
    PID=$(< $PIDFILE)
    if [ $? = 0 ]; then
        ps -p $PID >/dev/null 2>&1
        if [ $? = 0 ]; then
            echo "This script is (probably) already running as PID ${PID}."
            exit
        fi
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

cleanup() {
        rm $PIDFILE
}
trap cleanup EXIT

# Run command until we're killed.
while true; do
    eval "$@"
    echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
    sleep $TIMEOUT
done

使用法:

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C

このスクリプトを異なるディレクトリから実行すると、異なるpidfileが使用され、実行中の既存のインスタンスが検出されない場合があることに注意してください。引数を介して提供される一時的なコマンドを実行および再起動するように設計されているため、何かがすでに起動されているかどうかを知る方法はありません。同じコマンドであるかどうかは誰が言うのですか?何かの単一のインスタンスのみを実行するというこの強制を改善するには、状況に固有のソリューションが必要です。

また、それが適切なデーモンとして機能するためには、他の回答が言及しているように(最低限)nohupを使用する必要があります。プロセスが受信する可能性のあるシグナルに回復力を提供するための努力はしていません。

ノートを取るためにもう一つのポイントは、(それが呼び出された場合、さらに別の殺されたスクリプト、または信号で)このスクリプトを殺すことは子供がいる場合は特に、子供を殺すことに成功しないかもしれないということであるさらに別のスクリプトを実行します。なぜなのかevalはわかりませんが、作品の仕組みに関係しているようで、不思議です。したがって、その行を他の回答のように単一のコマンドのみを受け入れるものに置き換えるのが賢明な場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.