Ответ 1
Используйте средство системного демона, например start-stop-daemon.
В противном случае, да, где-то должен быть цикл.
Мне интересно, есть ли лучший способ сделать демона, который ждет чего-то, используя только sh, чем:
#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
echo "doing stuff"
}
while true; do
sleep 1000
done
В частности, мне интересно, есть ли способ избавиться от цикла и до сих пор слушать музыку.
Используйте средство системного демона, например start-stop-daemon.
В противном случае, да, где-то должен быть цикл.
Простое описание script (./myscript &
) не приведет к его демонизации. См. http://www.faqs.org/faqs/unix-faq/programmer/faq/, раздел 1.7, в котором описывается, что необходимо для того, чтобы стать демоном. Вы должны отключить его от терминала, чтобы SIGHUP
не убивал его. Вы можете сделать ярлык, чтобы заставить script отображаться как демон,
nohup ./myscript 0<&- &>/dev/null &
выполнит эту работу. Или, чтобы захватить stderr и stdout в файл:
nohup ./myscript 0<&- &> my.admin.log.file &
Однако могут быть и другие важные аспекты, которые необходимо учитывать. Например:
chdir("/")
(или cd /
внутри вашего script), и fork, чтобы родительский элемент вышел, и, следовательно, исходный дескриптор закрыт.umask 0
. Возможно, вы не захотите зависеть от umask вызывающего лица демона.Для примера script, который учитывает все эти аспекты, см. ответ Майка С..
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981
(./program.sh &) &
В некоторых из вышеперечисленных ответов отсутствуют некоторые важные части того, что делает демона демоном, а не только фоновым процессом, или фоновым процессом, отделенным от оболочки.
Этот http://www.faqs.org/faqs/unix-faq/programmer/faq/ описывает, что нужно, чтобы быть демоном. И этот Run bash script как демон реализует setid, хотя он пропускает chdir для root.
Исходный вопрос с плакатом был более конкретным, чем "Как создать процесс демона с помощью bash?", но так как тема и ответы обсуждают демонрование сценариев оболочки вообще, я думаю, что важно указать это (для нарушителей как я смотрю на мелкие детали создания демона).
Здесь мое исполнение оболочки script, которая будет вести себя в соответствии с 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" "[email protected]" </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" "[email protected]" &
$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 pretty.
# Kind of like, "The End" at the end of a movie that you
# already know is over. It 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
Это действительно зависит от того, что будет делать сам двоичный файл.
Например, я хочу создать несколько слушателей.
Начальный Daemon - простая задача:
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/)
теперь, как для слушателя, сам, Это должен быть какой-то цикл/оповещение или иначе, что вызовет script делать то, что хотите. Например, если вы хотите, чтобы ваш script спал 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
Итак, чтобы запустить UP:/tmp/deamon_test/startener start
и отправить команды из оболочки (или обернуть ее на script):
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)
Надеюсь, это поможет.
Посмотрите инструмент daemon из пакета libslack:
В Mac OS X для запуска оболочки используйте запускаемый script.
Если у меня был 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"
, и вы увидите, что ваш script все еще работает в фоновом режиме. Конечно, если вы хотите остановить его, выполните ту же команду (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
попробуйте выполнить с помощью & если вы сохраните этот файл как program.sh
вы можете использовать
$. program.sh &