任意の汎用スクリプトまたはコマンドを daemon に変換できるデーモン化ツールが欲しい。
対処したい2つの一般的なケースがあります。
永久に実行するスクリプトがあります。死ぬ(または再起動する)場合は、再起動します。一度に2つのコピーが実行されないようにしてください(コピーが既に実行されているかどうかを検出し、その場合は起動しないでください)。
シンプルなスクリプトまたはコマンドラインコマンドがあり、それを永久に繰り返し実行したい(実行の間に短い休止を入れて)。繰り返しますが、スクリプトの2つのコピーが一度に実行されることを許可しないでください。
もちろん、ケース2のスクリプトの周りに「while(true)」ループを記述してからケース1の解決策を適用するのは簡単ですが、ケース1のスクリプトに適用されるため、より一般的な解決策はケース2を直接解決しますスクリプトが死ぬことを意図していない場合は、短くするか、一時停止しないでください(もちろん、スクリプトが本当にdoes一時停止は実際には重要ではありません))。
ソリューションには、たとえば、ファイルロックコードまたはPID記録を既存のスクリプトに追加することを含めないでください。
より具体的には、次のように実行できるプログラムを「デーモン化」したい
% daemonize myscript arg1 arg2
または、たとえば、
% daemonize 'echo `date` >> /tmp/times.txt'
times.txtに追加される日付のリストが増え続けます。 (デーモン化する引数が上記のケース1のように永久に実行されるスクリプトである場合、デーモン化は依然として正しいことを行い、必要に応じて再起動することに注意してください。)上記のようなコマンドを.loginに入れることができますおよび/または1時間ごとまたは1分ごとにcronを実行します(予期せずに死んでしまうのを心配しているかどうかによって異なります)。
注意:daemonizeスクリプトは、デーモン化するコマンド文字列を記憶する必要があるため、同じコマンド文字列が再度デーモン化された場合、2番目のコピーは起動しません。
また、このソリューションはOS XとLinuxの両方で理想的に機能するはずですが、どちらか一方のソリューションは大歓迎です。
編集:Sudo daemonize myscript myargs
で呼び出す必要がある場合は問題ありません。
(これをすべて間違っていると思っている場合や、手早く汚い部分的な解決策がある場合は、それも聞きたいです。)
PS:役に立つ場合は、 here's Pythonに固有の同様の質問です。
this 同様の質問への回答には、任意のスクリプトをすばやく悪用するための便利なイディオムと思われるものがあります。
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)と同じファイルにリダイレクトするようシェルに指示します。
コマンドを一度だけ実行し、コマンドが死んだ場合に再起動するには、次のスクリプトを使用できます。
#!/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 &
長い回答をおaびします(私の回答が仕様にどのように影響するかについてのコメントをご覧ください)。私は包括的になろうとしているので、あなたは可能な限り脚を上げています。 :-)
プログラムをインストールでき(ルートアクセスがあり)、デーモンを実行するためのスクリプトをセットアップするために1回限りのレッグワークを実行する場合(つまり、コマンドラインで実行するコマンドライン引数を指定するだけでなく、しかし、サービスごとに1回だけ行う必要があります)、より堅牢な方法があります。
daemontools を使用する必要があります。投稿の残りでは、daemontoolsを使用してサービスをセットアップする方法について説明します。
/service
というディレクトリを作成します。インストーラーは既にこれを実行しているはずですが、確認するか、手動でインストールする場合のみです。この場所が気に入らない場合は、svscanboot
スクリプトで変更できますが、ほとんどのdaemontoolsユーザーは/service
の使用に慣れており、使用しないと混乱します。init
を使用しない(つまり、/etc/inittab
を使用しない)別のディストリビューションを使用している場合、プリインストールされたinittab
を使用する必要がありますsvscanboot
によって呼び出されるinit
を配置するためのベース。難しいことではありませんが、OSが使用するinit
の設定方法を知る必要があります。 svscanboot
は、svscan
を呼び出すスクリプトです。これは、サービスを探す主な作業を行います。 init
から呼び出されるため、init
は何らかの理由で死んだ場合に再起動するように手配します。/var/lib/svscan
を使用しますが、新しい場所は問題ありません。私は通常 スクリプト を使用してサービスディレクトリを設定し、手作業の繰り返し作業を大幅に節約します。例えば。、
Sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"
ここで、some-service-name
はサービスに付ける名前、user
はそのサービスを実行するユーザー、loguser
はロガーを実行するユーザーです。 (ロギングについては少し説明しています。)
fghack
を読んでください。ただし、これにはトレードオフがあります。svc
を使用してプログラムを制御することはできません。run
スクリプトを編集して、希望どおりに動作することを確認します。サービスが頻繁に終了すると予想される場合は、sleep
呼び出しを上部に配置する必要がある場合があります。/service
にサービスディレクトリを指すシンボリックリンクを作成します。 (サービスディレクトリを/service
内に直接配置しないでください。svscan
のウォッチからサービスを削除するのが難しくなります。)mkservice
で生成されたスクリプトを使用している場合は標準エラー)にログメッセージを書き込ませることです。 svscan
は、ログメッセージをログサービスに送信します。mkservice
によって生成されたログサービススクリプトは、log/main
ディレクトリに自動ローテーションされたタイムスタンプ付きログファイルを作成します。現在のログファイルの名前はcurrent
です。tai64nlocal
を介してログファイルをパイピングすると、タイムスタンプが人間が読める形式に変換されます。 (TAI64Nは、ナノ秒カウントの64ビットのアトミックタイムスタンプです。)svstat
を使用して、サービスのステータスを取得します。ロギングサービスは独立しており、独自のステータスを持っていることに注意してください。svc
を使用して、サービスを制御(開始、停止、再起動など)します。たとえば、サービスを再起動するには、svc -t /service/some-service-name
;を使用します。 -t
は、「SIGTERM
を送信」を意味します。-h
(SIGHUP
)、-a
(SIGALRM
)、-1
(SIGUSR1
)、-2
(SIGUSR2
)、および-k
(SIGKILL
)。-d
を使用します。サービスディレクトリにdown
という名前のファイルを作成することにより、起動時にサービスが自動的に開始しないようにすることもできます。-u
を使用します。これは、以前にダウンした(または自動起動しないように設定した)場合を除き、必要ありません。-x
;を使用します。通常、-d
とともに使用して、サービスも終了します。これはサービスの削除を許可する通常の方法ですが、最初に/service
からサービスのリンクを解除する必要があります。そうしないと、svscan
がスーパーバイザーを再起動します。また、ロギングサービス(mkservice -l
)を使用してサービスを作成した場合は、サービスディレクトリを削除する前にロギングスーパーバイザー(たとえば、svc -dx /var/lib/svscan/some-service-name/log
)も終了することを忘れないでください。長所:
init
が提供するものと同じ環境、プロセス制限などで実行されます。短所:
svc
を使用してサービスを再起動する必要があり、実行スクリプトを直接実行することはできません(これらのサービスはスーパーバイザーの制御下にないため)。supervise
プロセスが表示されます。)バランスとして、daemontoolsはあなたのニーズに最適なシステムだと思います。設定と保守の方法に関する質問は歓迎します。
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
start-stop-daemon(8)
を試してみたいと思うかもしれません。 /etc/init.d
Linuxディストリビューションの例では。起動されたプロセスは、呼び出されたコマンドラインまたはPIDファイルで検出できるため、スクリプトのウォッチドッグであることを除き、すべての要件に一致します。ただし、必要に応じてスクリプトを再起動するだけの別のデーモンウォッチドッグスクリプトをいつでも開始できます。
すでに説明したdaemonize
およびdaemontools
の代替として、libslackパッケージの daemon コマンドがあります。
daemon
は非常に構成可能であり、自動再起動、ロギング、またはpidfile処理などの退屈なデーモンのすべてを気にします。
特にOS Xを使用している場合は、launchdの動作を確認することをお勧めします。スクリプトが実行されていることを自動的に確認し、必要に応じて再起動します。また、あらゆる種類のスケジューリング機能などが含まれます。要件1と2の両方を満たす必要があります。
スクリプトのコピーを1つだけ実行できるようにするには、PIDファイルを使用する必要があります。通常、現在実行中のインスタンスのPIDを含むファイルを/var/run/.pidに書き込みます。プログラムの実行時にファイルが存在する場合、ファイル内のPIDが実際に実行されているかどうかを確認します(プログラムがクラッシュしたか、PIDファイルの削除を忘れた可能性があります)。存在する場合、中止します。そうでない場合は、実行を開始してPIDファイルを上書きします。
Daemontools( http://cr.yp.to/daemontools.html )は、dj bernsteinによって作成された、これを行うために使用されるかなりハードコアなユーティリティのセットです。私はこれを使って成功しました。面倒な部分は、スクリプトを実行しても目に見える結果が返されないことです-目に見えない戻りコードだけです。しかし、一度実行されると防弾になります。
最初にcreateDaemon()
を http://code.activestate.com/recipes/278731/ から取得します
次に、メインコード:
import subprocess
import sys
import time
createDaemon()
while True:
subprocess.call(" ".join(sys.argv[1:]),Shell=True)
time.sleep(10)
Monit を試すこともできます。 Monitは、他のサービスを監視および報告するサービスです。主に実行時の問題を(電子メールとsmsを介して)通知する方法として使用されますが、ここで提案されている他のほとんどの提案を実行することもできます。プログラムの自動(再)起動と停止、電子メールの送信、他のスクリプトの開始、および取得可能な出力のログの維持が可能です。さらに、ドキュメントが充実しているため、インストールと保守が簡単であることがわかりました。
これは、空のディレクトリにコピーして試すことができる作業バージョンです(CPAN依存関係をインストールした後、 Getopt :: Long 、 File :: Spec 、 File :: Pid 、および IPC :: System :: Simple -すべてが非常に標準的であり、すべてのハッカーに強くお勧めします:一度にインストールできます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
不滅 これは* nixクロスプラットフォーム(OSに依存しない)スーパーバイザーです。
MacOSを簡単に試すには:
brew install immortal
ポートから FreeBSD を使用している場合、またはpkgを使用している場合:
pkg install immortal
Linux の場合、プリコンパイル済みバイナリをダウンロードするか、ソースから: https://immortal.run/source/
次のように使用できます。
immortal -l /var/log/date.log date
または、より多くのオプションを提供する configuration 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
その他の回答 について一連の改善を行いました。
sleep
に送信)-h
の使用プロンプトeval
に送信されるため、任意の種類のシェルスクリプトをこのスクリプトに送信する文字列として作成できます。デーモン化する最後の引数(または末尾の引数)として-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を使用する必要があります。プロセスが受信する可能性のある信号に回復力を提供する努力はしていません。
注意すべきもう1つの点は、このスクリプトを(強制終了された別のスクリプトから、またはシグナルで呼び出された場合に)強制終了すると、特にその子がまだ別の場合スクリプト。これがなぜなのかはわかりませんが、eval
の動作に関連するもののようです。したがって、その行を、他の回答のように単一のコマンドのみを受け入れるものに置き換えるのが賢明かもしれません。