शेल स्क्रिप्ट डेमॉन बनाने का सबसे अच्छा तरीका है?


82

मैं सोच रहा था कि क्या एक डेमन बनाने का एक बेहतर तरीका है जो केवल श की तुलना में किसी चीज़ का इंतजार कर रहा है:

#! /bin/sh
trap processUserSig SIGUSR1
processUserSig() {
  echo "doing stuff"
}

while true; do
  sleep 1000
done

विशेष रूप से, मैं सोच रहा हूं कि क्या लूप से छुटकारा पाने का कोई तरीका है और अभी भी संकेतों के लिए बात सुनना है।


3
आपको एक लूप की आवश्यकता होगी, लेकिन ध्यान दें कि आपका उदाहरण संभवतः उस तरीके से प्रदर्शन करने वाला नहीं है जिसकी आप अपेक्षा करते हैं। नींद एक शेल बिल्डिन नहीं है, और शेल द्वारा प्राप्त एक SIGUSR1 बाल प्रक्रियाओं के लिए प्रचारित नहीं है। इस प्रकार आपका सिग्नल हैंडलर तब तक संसाधित नहीं होगा जब तक कि नींद पूरी नहीं हो जाती। Mywiki.wooledge.org/SignalTrap#preview , तीसरा खंड देखें ।
माइक एस

जवाबों:


49

अपने सिस्टम की डेमन सुविधा का उपयोग करें, जैसे कि स्टार्ट-स्टॉप-डेमॉन

नहीं तो, हाँ, वहाँ कहीं न कहीं एक पाश होना चाहिए।


2
स्टार्ट-स्टॉप-डेमॉन का उपयोग करने के लिए आश्चर्यजनक रूप से सुविधाजनक था
mpartel

URL अब मान्य नहीं है।
एरॉन

119

बस अपनी स्क्रिप्ट ( ./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। आप डेमन के कॉलर के उम्मा पर निर्भर नहीं होना चाह सकते हैं।

एक स्क्रिप्ट के उदाहरण के लिए जो इन सभी पहलुओं को ध्यान में रखता है, माइक एस का जवाब देखें


1
सबसे पहले, इस उत्तर के लिए धन्यवाद। यह ज्यादातर मेरे लिए बहुत अच्छा काम कर रहा है। लेकिन मैं लॉग फ़ाइल में संलग्न करना चाहूंगा और जब मैं कोशिश करता हूं "और >> log.txt" मुझे यह त्रुटि मिलती है ... "अप्रत्याशित टोकन के पास सिंटैक्स त्रुटि"> "मेरे लिए कोई विचार?"
टॉम स्ट्रैटन

7
क्या करता 0<&-है? यह स्पष्ट नहीं है कि पात्रों का वह क्रम क्या पूरा करता है।
क्रेग मैकक्वीन

13
यह स्पष्ट नहीं है कि क्या 0<&-करना है। मुझे यह लिंक मिला जो इसे समझाता है।
क्रेग मैकक्यून

2
यह सही है, 0<&-स्टड बंद कर देता है (एफडी 0)। इस तरह, यदि आपकी प्रक्रिया गलती से स्टडिन (आसान करने के लिए) से पढ़ती है, तो इसे लटकाए जाने के बजाय एक त्रुटि मिलेगी जो डेटा दिखाने के लिए इंतजार कर रही है।
ब्रॉनसन

1
nohup स्वचालित रूप से / dev / null से stdin को रीडायरेक्ट करता है (मैनुअल देखें)। एसटीडी फाइल डिस्क्रिप्टर को बंद करना सबसे अच्छा अभ्यास नहीं है।
विक

75

शीर्ष-अपवृत्त उत्तरों में से कुछ यहां कुछ महत्वपूर्ण भागों को याद कर रहे हैं जो एक डेमॉन को एक डेमन बनाते हैं, जैसा कि सिर्फ एक पृष्ठभूमि प्रक्रिया, या एक शेल से अलग की गई पृष्ठभूमि प्रक्रिया के विपरीत है।

यह http://www.faqs.org/faqs/unix-faq/programmer/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

यह बहुत अच्छा जवाब है! क्या आपको (माइक एस) एसओ (ब्लॉग, सोशल नेटवर्क, जो भी हो) के अलावा किसी भी तरह की ऑनलाइन उपस्थिति है? मैंने आपकी प्रोफ़ाइल जानकारी पर ऐसा कुछ नहीं देखा।
मिशैल ले बारबियर

@ माइकलग्रुवेनल्ड औपचारिक रूप से ज्यादा नहीं। मुझे डर है कि मैं दिलचस्प नहीं हूँ; मैं ज्यादा ब्लॉग नहीं करता। मेरे पास schwager.com डोमेन है (जो इस समय बहुत कम है), और मेरे पास कुछ Arduino प्रोजेक्ट्स हैं। आम तौर पर मेरा नामित डे प्लम ग्रेनेगोम है। आप Google GreyGnome या GreyGnome + Arduino कर सकते हैं और मुझे चारों ओर देख सकते हैं।
माइक एस।

1
@ माइक धन्यवाद! मैं पूछ रहा हूं क्योंकि शेल प्रोग्रामिंग में रुचि रखने वाले लोगों से मिलना इतना आम नहीं है, और मैं हमेशा इस बारे में विचारों और विचारों का आदान-प्रदान करने में प्रसन्न हूं। उस पूरक जानकारी के लिए धन्यवाद! :)
मिशैल ले बारबियर

@ मायके, आपको दो बार कांटा लगाने की आवश्यकता क्यों है? आपके कोड में मूल स्क्रिप्ट का पोता सेटसाइड के साथ नया सत्र नेता बन जाता है, हां? आप पहले कांटे के लिए ऐसा क्यों नहीं कर सकते?
प्रॉमेटी

एफएक्यू से मैंने अपनी पोस्टिंग में संदर्भित किया: "यहां एक डेमन बनने के चरण हैं: ... 1. fork()' so the parent can exit, this returns control to the command line or shell invoking your program. ... 2. सेटसाइड () 'एक प्रक्रिया समूह और सत्र समूह लीडर बनने के लिए ... हमारी प्रक्रिया में अब कोई नियंत्रण टर्मिनल नहीं है, जो डेमन के लिए एक अच्छी बात ... 3. `कांटा () 'फिर से माता पिता ... बाहर निकल सकते हैं। इसका मतलब है कि हम, एक गैर-सत्र समूह के नेता के रूप में, कभी भी एक नियंत्रित टर्मिनल हासिल नहीं कर सकते हैं।"
माइक एस

63
# double background your script to have it detach from the tty
# cf. http://www.linux-mag.com/id/5981 
(./program.sh &) & 

शांत चाल! मैं आमतौर पर नोह के साथ जाता हूं, लेकिन मैं निश्चित रूप से इसके लिए उपयोग करूंगा।
joel

महान! मुझे नहीं पता कि यह उचित तरीका है, लेकिन यह एक आकर्षण की तरह काम करता है।
मार्क MAURICE

1
यही कारण है कि डिस्कनेक्ट करने के लिए प्रतीत नहीं होता है stdin, stdout, stderr। कम से कम साथ नहीं sh
क्रेग मैकक्वीन

3
डिटैचिंग के लिए इस विधि का काफी उपयोग किया जाता है। इसे "डबल कांटा" कहा जाता है और पवित्र UNIX शास्त्रों में से एक, 2 स्टीवंस ( amazon.com/dp/0201433079 ) में अधिक गहराई से समझाया गया है ।
डेव

1
डबल कांटा और सेटिड्स () के बारे में अधिक तकनीकी जानकारी के लिए, देखें thelinuxjedi.blogspot.com/2014/02// । विशेष रूप से "द ब्रेकडाउन" अनुभाग पढ़ें, जहां यह कहता है, "डबल-फोर्क के पीछे असली कदम इस प्रकार हैं:"
माइक एस

4

यह वास्तव में इस बात पर निर्भर करता है कि बाइनरी खुद क्या करने जा रहा है।

उदाहरण के लिए मैं कुछ श्रोता बनाना चाहता हूं।

शुरुआती डेमन सरल कार्य है:

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

यहाँ सरल श्रोता है कि आप ऐसा कर सकते हैं जो दूरस्थ मशीन से आपके आदेशों को सुनेगा और उन्हें स्थानीय पर क्रियान्वित करेगा:

श्रोता:

#!/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 / श्रोता शुरू करें

और शेल से कमांड भेजने के लिए (या इसे स्क्रिप्ट में लपेटें):

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)

आशा है कि यह मदद करेगा।




1

अगर मेरे पास था script.shऔर मैं इसे बैश से निष्पादित करना चाहता था और इसे तब भी चलाना छोड़ देता हूं जब मैं अपना बैश सत्र बंद करना चाहता हूं, तो मैं इसे प्राप्त करूंगा nohupऔर& अंत में।

उदाहरण: nohup ./script.sh < inputFile.txt > ./logFile 2>&1 &

inputFile.txtकोई भी फ़ाइल हो सकती है यदि आपकी फ़ाइल में कोई इनपुट नहीं है तो हम आमतौर पर उपयोग करते हैं /dev/null। तो आदेश होगा:

nohup ./script.sh < /dev/null > ./logFile 2>&1 &

उसके बाद अपना बैश सत्र बंद करें, दूसरा टर्मिनल खोलें और निष्पादित करें: ps -aux | egrep "script.sh"और आप देखेंगे कि आपकी स्क्रिप्ट अभी भी पृष्ठभूमि पर चल रही है। यदि आप इसे रोकना चाहते हैं, तो एक ही कमांड (पीएस) निष्पादित करेंkill -9 <PID-OF-YOUR-SCRIPT>


0

बैश सेवा प्रबंधक देखें परियोजना : 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

0

कई जवाबों की तरह यह एक "वास्तविक" विमुद्रीकरण नहीं है, बल्कि एक विकल्प है nohup दृष्टिकोण का ।

echo "script.sh" | at now

स्पष्ट रूप से उपयोग करने से मतभेद हैं nohup । एक के लिए पहली जगह में माता-पिता से कोई कोचिंग नहीं है। इसके अलावा "script.sh" माता-पिता के वातावरण को विरासत में नहीं देता है।

किसी भी तरह से यह एक बेहतर विकल्प नहीं है। यह पृष्ठभूमि में प्रक्रियाओं को लॉन्च करने का एक अलग (और कुछ हद तक आलसी) तरीका है।

PS मैंने व्यक्तिगत रूप से कार्लो के उत्तर को उकेरा क्योंकि यह सबसे सुरुचिपूर्ण लगता है और टर्मिनल और स्क्रिप्ट के अंदर से दोनों काम करता है


0

बॉर्न शेल (या बैश) में एक वैध डेमॉन बनाने के मूल प्रस्ताव के लिए यहां न्यूनतम परिवर्तन है:

#!/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

स्पष्टीकरण:

  • लाइन 2-7: एक डेमॉन को फोर्क किया जाना चाहिए ताकि उसके पास माता-पिता न हों। अंतहीन जाली को रोकने के लिए एक कृत्रिम तर्क का उपयोग करना। "सेटिड" प्रक्रिया और टर्मिनल शुरू करने से अलग हो जाता है।
  • पंक्ति 9: हमारे वांछित संकेत को अन्य संकेतों से अलग करने की आवश्यकता है।
  • पंक्ति 10: "नींद" प्रक्रियाओं को झूलने से छुटकारा पाने के लिए सफाई की आवश्यकता होती है।
  • पंक्ति ११-१३: स्क्रिप्ट के पुनर्निर्देशन, स्टडर और स्टडिन।
  • पंक्ति 16: पृष्ठभूमि में सोएं
  • पंक्ति 18: प्रतीक्षा नींद के अंत की प्रतीक्षा करती है, लेकिन (कुछ) संकेतों से बाधित हो जाती है।
  • लाइन 19: नींद की प्रक्रिया को मारें, क्योंकि सिग्नल पकड़े जाने पर यह अभी भी चल रहा है।
  • पंक्ति 22: यदि SIGUSR1 पकड़ा गया है तो कार्य करें।

लगता है कि यह किसी भी सरल नहीं मिलता है।


-3

का उपयोग करके निष्पादित करने का प्रयास करें और यदि आप इस फ़ाइल को प्रोग्राम.श के रूप में सहेजते हैं

आप उपयोग कर सकते हैं

$. program.sh &
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.