Лучший способ сделать демон сценария оболочки?
Мне интересно, есть ли лучший способ сделать демона, который ждет чего-то, используя только sh, чем:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
в частности, мне интересно, есть ли какой-либо способ избавиться от петли и все еще есть вещь, которая слушает сигналы.
11 ответов:
используйте средство демона вашей системы, например 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 вызывающего объекта демон.пример сценария, который учитывает все эти аспекты, см. В разделе Майк S ' ответ.
# double background your script to have it detach from the tty # cf. http://www.linux-mag.com/id/5981 (./program.sh &) &
в некоторых из ответов top-upoted здесь отсутствуют некоторые важные части того, что делает демона демоном, а не просто фоновым процессом или фоновым процессом, отделенным от оболочки.
это http://www.faqs.org/faqs/unix-faq/programmer/faq/ описывает, что необходимо, чтобы быть демоном. А это запустите скрипт bash как демон реализует setsid, хотя он пропускает chdir для root.
изначально вопрос был на самом деле более конкретно, чем "как создать процесс демона с помощью bash?", но поскольку тема и ответы обсуждают демонизирующие сценарии оболочки в целом, я думаю, что важно указать на это (для таких злоумышленников, как я, изучающих мелкие детали создания демона).
вот мое представление сценария оболочки, который будет вести себя в соответствии с FAQ. Набор отладки
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=""; tty="" [[ "$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 ) cd / #### CHILD HERE ---------------------------------------------------------------------> if [ "" = "child" ] ; then # 2. We are the child. We need to fork again. shift; tty=""; 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 [ "" != "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=""; 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
. Обратите внимание, как меняются номера идентификаторов групп сеансов и процессов (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
это действительно зависит от того, что будет делать сам двоичный файл.
например, я хочу создать какой-то слушатель.
запуск демона-это простая задача:
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 "" in start) echo -n "Starting Listener Deamon .... " startproc -f -p $PIDFILE $LISTENER_BIN echo "running" ;; *) echo "Usage: start" exit 1 ;; esac
вот как мы запускаем демон (общий способ для всех /etc / init.д/ сотрудников)
теперь что касается самого слушателя, Это должен быть какой-то цикл / предупреждение, иначе это вызовет скрипт делать то, что ты хочешь. Например, если вы хотите, чтобы ваш скрипт спал 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 для демона оболочки.
если бы у меня был
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="" # 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="" # Action echo "* Starting with $action" } after-finish() { local action="" # Action local serviceExitCode= # Service exit code echo "* Finish with $action. Exit code: $serviceExitCode" } action="" 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-не наследует родительское окружение.ни в коем случае это лучшая альтернатива. Это просто другой (и несколько ленивый) способ запуска процессов в фоновом режиме.
P. S. Я лично поддержанный ответ Карло, как кажется, самый элегантный и работает как с терминала, так и внутри скриптов