Shのみを使用して何かを待つデーモンを作成するより良い方法があるかどうか疑問に思っています:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
特に、ループを取り除いても信号をリッスンする方法があるかどうか疑問に思っています。
start-stop-daemon などのシステムのデーモン機能を使用します。
そうでなければ、はい、どこかにループがなければなりません。
スクリプト(./myscript &
)をバックグラウンド化するだけでは、デーモン化されません。 http://www.faqs.org/faqs/unix-faq/programmer/faq/ 、セクション1.7を参照してください。これは、デーモンになるために必要なものを説明しています。 SIGHUP
が強制終了しないように、ターミナルから切断する必要があります。ショートカットを使用して、スクリプトをデーモンのように動作させることができます。
Nohup ./myscript 0<&- &>/dev/null &
仕事をします。または、stderrとstdoutの両方をファイルにキャプチャするには:
Nohup ./myscript 0<&- &> my.admin.log.file &
ただし、考慮する必要があるさらに重要な側面がある場合があります。例えば:
chdir("/")
(またはスクリプト内でcd /
)し、forkして親が終了するようにして、元の記述子を閉じます。umask 0
を実行してください。デーモンの呼び出し元のumaskに依存したくない場合があります。これらすべての側面を考慮したスクリプトの例については、 Mike S 'answer を参照してください。
ここで最も支持された回答のいくつかは、単なるバックグラウンドプロセスまたはシェルから切り離されたバックグラウンドプロセスではなく、デーモンをデーモンにする重要な部分を欠いています。
この http://www.faqs.org/faqs/unix-faq/programmer/faq/ は、デーモンになるために必要なものを説明しています。そして、この bashスクリプトをデーモンとして実行する は、setidを実装しますが、ルートへのchdirを逃します。
元のポスターの質問は、実際には「bashを使用してデーモンプロセスを作成する方法」よりも具体的でしたが、主題と回答は一般にシェルスクリプトのデーモン化について説明しているため、それを指摘することが重要だと思います(私のような侵入者にとってはデーモン作成の詳細)。
FAQに従って動作するシェルスクリプトのレンディションを次に示します。 DEBUGをtrue
に設定すると、きれいな出力が表示されます(ただし、無限にループするのではなく、すぐに終了します)。
#!/bin/bash
DEBUG=false
# This part is for fun, if you consider Shell scripts fun- and I do.
trap process_USR1 SIGUSR1
process_USR1() {
echo 'Got signal USR1'
echo 'Did you notice that the signal was acted upon only after the sleep was done'
echo 'in the while loop? Interesting, yes? Yes.'
exit 0
}
# End of fun. Now on to the business end of things.
print_debug() {
whatiam="$1"; tty="$2"
[[ "$tty" != "not a tty" ]] && {
echo "" >$tty
echo "$whatiam, PID $$" >$tty
ps -o pid,sess,pgid -p $$ >$tty
tty >$tty
}
}
me_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
me_FILE=$(basename $0)
cd /
#### CHILD HERE --------------------------------------------------------------------->
if [ "$1" = "child" ] ; then # 2. We are the child. We need to fork again.
shift; tty="$1"; shift
$DEBUG && print_debug "*** CHILD, NEW SESSION, NEW PGID" "$tty"
umask 0
$me_DIR/$me_FILE XXrefork_daemonXX "$tty" "$@" </dev/null >/dev/null 2>/dev/null &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "CHILD OUT" >$tty
exit 0
fi
##### ENTRY POINT HERE -------------------------------------------------------------->
if [ "$1" != "XXrefork_daemonXX" ] ; then # 1. This is where the original call starts.
tty=$(tty)
$DEBUG && print_debug "*** PARENT" "$tty"
setsid $me_DIR/$me_FILE child "$tty" "$@" &
$DEBUG && [[ "$tty" != "not a tty" ]] && echo "PARENT OUT" >$tty
exit 0
fi
##### RUNS AFTER CHILD FORKS (actually, on Linux, clone()s. See strace -------------->
# 3. We have been reforked. Go to work.
exec >/tmp/outfile
exec 2>/tmp/errfile
exec 0</dev/null
shift; tty="$1"; shift
$DEBUG && print_debug "*** DAEMON" "$tty"
# The real stuff goes here. To exit, see fun (above)
$DEBUG && [[ "$tty" != "not a tty" ]] && echo NOT A REAL DAEMON. NOT RUNNING WHILE LOOP. >$tty
$DEBUG || {
while true; do
echo "Change this loop, so this silly no-op goes away." >/dev/null
echo "Do something useful with your life, young man." >/dev/null
sleep 10
done
}
$DEBUG && [[ "$tty" != "not a tty" ]] && sleep 3 && echo "DAEMON OUT" >$tty
exit # This may never run. Why is it here then? It's pretty.
# Kind of like, "The End" at the end of a movie that you
# already know is over. It's always Nice.
DEBUG
がtrue
に設定されている場合、出力は次のようになります。セッションおよびプロセスグループID(SESS、PGID)番号がどのように変化するかに注目してください。
<Shell_Prompt>$ bash blahd
*** PARENT, PID 5180
PID SESS PGID
5180 1708 5180
/dev/pts/6
PARENT OUT
<Shell_Prompt>$
*** CHILD, NEW SESSION, NEW PGID, PID 5188
PID SESS PGID
5188 5188 5188
not a tty
CHILD OUT
*** DAEMON, PID 5198
PID SESS PGID
5198 5188 5188
not a tty
NOT A REAL DAEMON. NOT RUNNING WHILE LOOP.
DAEMON OUT
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981
(./program.sh &) &
バイナリ自体が何をしようとしているかに本当に依存します。
たとえば、リスナーを作成したいです。
開始デーモンは簡単なタスクです:
lis_deamon:
#!/bin/bash
# We will start the listener as Deamon process
#
LISTENER_BIN=/tmp/deamon_test/listener
test -x $LISTENER_BIN || exit 5
PIDFILE=/tmp/deamon_test/listener.pid
case "$1" in
start)
echo -n "Starting Listener Deamon .... "
startproc -f -p $PIDFILE $LISTENER_BIN
echo "running"
;;
*)
echo "Usage: $0 start"
exit 1
;;
esac
これがデーモンの起動方法です(すべての/etc/init.d/スタッフに共通の方法)
リスナー自体については、何らかのループ/アラートであるか、スクリプトをトリガーして必要な処理を行う必要があります。たとえば、スクリプトを10分間スリープさせて目を覚まし、どのように実行しているかを尋ねる場合は、
while true ; do sleep 600 ; echo "How are u ? " ; done
以下は、リモートマシンからのコマンドをリッスンし、ローカルで実行するuの簡単なリスナーです。
リスナー:
#!/bin/bash
# Starting listener on some port
# we will run it as deamon and we will send commands to it.
#
IP=$(hostname --ip-address)
PORT=1024
FILE=/tmp/backpipe
count=0
while [ -a $FILE ] ; do #If file exis I assume that it used by other program
FILE=$FILE.$count
count=$(($count + 1))
done
# Now we know that such file do not exist,
# U can write down in deamon it self the remove for those files
# or in different part of program
mknod $FILE p
while true ; do
netcat -l -s $IP -p $PORT < $FILE |/bin/bash > $FILE
done
rm $FILE
起動するには:/ tmp/deamon_test/listener start
シェルからコマンドを送信する(またはスクリプトにラップする):
test_Host#netcat 10.184.200.22 1024
uptime
20:01pm up 21 days 5:10, 44 users, load average: 0.62, 0.61, 0.60
date
Tue Jan 28 20:02:00 IST 2014
punt! (Cntrl+C)
これが役立つことを願っています。
Libslackパッケージのデーモンツールをご覧ください。
Mac OS Xでは、Shellデーモン用のlaunchdスクリプトを使用します。
script.sh
があり、bashセッションを閉じたい場合でもbashから実行して実行したい場合は、最後にNohup
と&
を組み合わせます。
例:Nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &
inputFile.txt
は任意のファイルです。ファイルに入力がない場合、通常は/dev/null
を使用します。したがって、コマンドは次のようになります。
Nohup ./script.sh < /dev/null > ./logFile 2>&1 &
Bashセッションを閉じた後、別のターミナルを開いてps -aux | egrep "script.sh"
を実行すると、スクリプトがまだバックグラウンドで実行されていることがわかります。もちろん、停止する場合は、同じコマンド(ps)とkill -9 <PID-OF-YOUR-SCRIPT>
を実行します
多くの回答のように、これは「本当の」デーモン化ではなく、Nohup
アプローチの代替です。
echo "script.sh" | at now
Nohup
の使用とは明らかに違いがあります。そもそも、親からの分離はありません。また、「script.sh」は親の環境を継承しません。
決してこれがより良い代替手段ではありません。これは、単にバックグラウンドでプロセスを起動する別の(やや面倒な)方法です。
追伸カルロの答えは、最もエレガントであると思われ、端末と内部スクリプトの両方で動作するため、個人的に支持しました
Bash Service Managerプロジェクトを参照してください: https://github.com/reduardo7/bash-service-manager
#!/usr/bin/env bash
export PID_FILE_PATH="/tmp/my-service.pid"
export LOG_FILE_PATH="/tmp/my-service.log"
export LOG_ERROR_FILE_PATH="/tmp/my-service.error.log"
. ./services.sh
run-script() {
local action="$1" # Action
while true; do
echo "@@@ Running action '${action}'"
echo foo
echo bar >&2
[ "$action" = "run" ] && return 0
sleep 5
[ "$action" = "debug" ] && exit 25
done
}
before-start() {
local action="$1" # Action
echo "* Starting with $action"
}
after-finish() {
local action="$1" # Action
local serviceExitCode=$2 # Service exit code
echo "* Finish with $action. Exit code: $serviceExitCode"
}
action="$1"
serviceName="Example Service"
serviceMenu "$action" "$serviceName" run-script "$workDir" before-start after-finish
$ ./example-service
# Actions: [start|stop|restart|status|run|debug|tail(-[log|error])]
$ ./example-service start
# Starting Example Service service...
$ ./example-service status
# Serive Example Service is runnig with PID 5599
$ ./example-service stop
# Stopping Example Service...
$ ./example-service status
# Service Example Service is not running
$ ( cd /; umask 0; setsid your_script.sh </dev/null &>/dev/null & ) &