sh 만 사용하여 무언가를 기다리는 데몬을 만드는 더 좋은 방법이 있는지 궁금합니다.
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
특히 루프를 제거하고 신호를 수신 할 수있는 방법이 있는지 궁금합니다.
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 /
스크립트 내부에서) 부모가 종료되도록 포크하여 원래 설명자가 닫힙니다.umask 0
. 데몬 호출자의 umask에 의존하고 싶지 않을 수 있습니다.이러한 모든 측면을 고려한 스크립트의 예는 Mike S의 답변을 참조하십시오 .
0<&-
합니까? 일련의 문자가 무엇을 성취하는지는 분명하지 않습니다.
0<&-
는 분명 하지 않습니다. 나는 그것을 설명하는 이 링크 를 찾았 습니다.
0<&-
stdin (fd 0)을 닫습니다. 이렇게하면 프로세스가 실수로 stdin에서 읽은 경우 (쉽게 수행 할 수 있음) 데이터가 표시 될 때까지 계속 기다리지 않고 오류가 발생합니다.
여기에서 가장 많이 뽑힌 답변 중 일부는 백그라운드 프로세스 또는 셸에서 분리 된 백그라운드 프로세스와 달리 데몬을 데몬으로 만드는 중요한 부분이 누락되었습니다.
이 http://www.faqs.org/faqs/unix-faq/programmer/faq/는 데몬으로 필요한 것을 설명합니다. 그리고이 Run bash script as daemon 은 setsid를 구현하지만 루트에 대한 chdir이 누락되었습니다.
원래 포스터의 질문은 실제로 "Bash를 사용하여 데몬 프로세스를 만드는 방법"보다 더 구체적 이었지만 주제와 답변은 일반적으로 쉘 스크립트 데몬 화에 대해 논의하기 때문에 지적하는 것이 중요하다고 생각합니다. 데몬 생성에 대한 세부 정보).
다음은 FAQ에 따라 작동하는 쉘 스크립트의 제 표현입니다. true
예쁜 출력을 보려면 DEBUG를 로 설정 하십시오 (그러나 끝없이 반복되는 대신 즉시 종료됩니다).
#!/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 padawan." >/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
fork()' so the parent can exit, this returns control to the command line or shell invoking your program. ... 2.
setsid () '프로세스 그룹 및 세션 그룹 리더가됩니다. 이제 프로세스에 제어 터미널이 없습니다. 데몬에게 좋은 것 ... 3.`fork () '다시 부모가 종료 할 수 있도록 ... 이것은 우리가 비 세션 그룹 리더로서 제어 터미널을 다시 얻을 수 없음을 의미합니다. "
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981
(./program.sh &) &
stdin
, stdout
, stderr
. 적어도 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/ staff에 대한 일반적인 방법).
이제 리스너 자체는 일종의 루프 / 경고이거나 스크립트가 원하는 작업을 수행하도록 트리거해야합니다. 예를 들어 스크립트가 10 분 동안 잠자고 깨어나서 어떻게하고 있는지 묻는다면
while true ; do sleep 600 ; echo "How are u ? " ; done
다음은 원격 시스템에서 명령을 수신하고 로컬에서 실행할 수있는 간단한 리스너입니다.
리스너 :
#!/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에서는 셸 데몬에 launchd 스크립트를 사용합니다.
a가 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>
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
많은 답변과 마찬가지로 이것은 "실제"데몬 화가 아니라 nohup
접근 방식 의 대안 입니다.
echo "script.sh" | at now
를 사용하는 것과 분명히 다른 점이 있습니다 nohup
. 하나에게는 처음에 부모로부터 분리되는 것이 없습니다. 또한 "script.sh"는 부모의 환경을 상속하지 않습니다.
결코 이것이 더 나은 대안이 아닙니다. 단순히 백그라운드에서 프로세스를 시작하는 다른 (그리고 다소 게으른) 방법입니다.
추신 : 나는 개인적으로 carlo의 대답을 찬성했습니다. 가장 우아하고 터미널과 내부 스크립트 모두에서 작동합니다.
다음은 Bourne 쉘 (또는 Bash)에서 유효한 데몬을 생성하기위한 원래 제안에 대한 최소한의 변경 사항입니다.
#!/bin/sh
if [ "$1" != "__forked__" ]; then
setsid "$0" __forked__ "$@" &
exit
else
shift
fi
trap 'siguser1=true' SIGUSR1
trap 'echo "Clean up and exit"; kill $sleep_pid; exit' SIGTERM
exec > outfile
exec 2> errfile
exec 0< /dev/null
while true; do
(sleep 30000000 &>/dev/null) &
sleep_pid=$!
wait
kill $sleep_pid &>/dev/null
if [ -n "$siguser1" ]; then
siguser1=''
echo "Wait was interrupted by SIGUSR1, do things here."
fi
done
설명:
그것보다 더 간단하지 않다고 생각하십시오.