कई सबप्रोसेस के लिए बैश में इंतजार कैसे करें और बाहर निकलने के कोड को वापस करने के लिए! = 0 जब कोड के साथ कोई सबप्रोसेस खत्म हो जाए! = 0


561

उस स्क्रिप्ट से कई सबप्रोसेस के लिए बैश स्क्रिप्ट में प्रतीक्षा करने के लिए कैसे समाप्त कोड को वापस करने और वापस लौटने के लिए? = 0 जब कोड के साथ कोई भी उपप्रकार समाप्त होता है? = 0

सरल स्क्रिप्ट:

#!/bin/bash
for i in `seq 0 9`; do
  doCalculations $i &
done
wait

उपरोक्त स्क्रिप्ट सभी 10 स्पॉन्स्ड सबप्रोसेस के लिए प्रतीक्षा करेगी, लेकिन यह हमेशा एग्जिट स्टेटस 0 देगी (देखें help wait)। मैं इस लिपि को कैसे संशोधित कर सकता हूं ताकि यह स्पॉन्स्ड सबप्रोसेस की एग्जिट स्टेटस की खोज करे और एग्जिट कोड 1 लौटाए जब कोई भी सबप्रोसेसिस कोड के साथ समाप्त हो जाए! = 0!

क्या इसके लिए कोई बेहतर उपाय है कि उपप्रकारों के पीआईडी ​​एकत्र करने से, क्रम में उनके लिए प्रतीक्षा करें और निकास की स्थिति का योग करें?


1
इसे छूने के लिए काफी सुधार किया जा सकता है wait -n, केवल पहले / अगले आदेश के पूरा होने पर वापस लौटने के लिए आधुनिक बैश में उपलब्ध है।
चार्ल्स डफी

यदि आप बैश का उपयोग करके परीक्षण करना चाहते हैं, तो यह कोशिश करें: github.com/sstephenson/bats
अलेक्जेंडर मिल्स

2
BATS का सक्रिय विकास github.com/bats-core/bats-core
पर्थेका

3
@CharlesDuffy wait -nमें एक छोटी सी समस्या है: यदि कोई बाल रोजगार शेष नहीं है (उर्फ दौड़ की स्थिति), तो यह एक गैर-शून्य निकास स्थिति (असफल) देता है जो एक असफल बाल प्रक्रिया से अप्रभेद्य हो सकता है।
ड्रेविको

5
@CharlesDuffy - आपके पास अद्भुत अंतर्दृष्टि है, और आप इसे साझा करके SO को एक बड़ी सेवा करते हैं। ऐसा लगता है कि एसओ के लगभग 80% पोस्ट मैंने पढ़ा है कि आप ज्ञान के अद्भुत छोटे हीरे साझा कर रहे हैं जो कि अनुभव के विशाल महासागर से आने चाहिए। बहुत धन्यवाद!
ब्रेट होलमैन

जवाबों:


519

waitभी (वैकल्पिक रूप से) प्रक्रिया की पीआईडी ​​लेता है, और $ के साथ प्रतीक्षा करने के लिए! आपको पृष्ठभूमि में लॉन्च की गई अंतिम कमांड का पीआईडी ​​मिलता है। किसी भी उप-प्रक्रिया की PID को किसी सरणी में संग्रहीत करने के लिए लूप को संशोधित करें, और फिर प्रत्येक PID पर प्रतीक्षा कर रहा है।

# run processes and store pids in array
for i in $n_procs; do
    ./procs[${i}] &
    pids[${i}]=$!
done

# wait for all pids
for pid in ${pids[*]}; do
    wait $pid
done

9
वेइल, चूंकि आप सभी प्रक्रियाओं के लिए प्रतीक्षा करने जा रहे हैं, इससे कोई फर्क नहीं पड़ता जैसे कि आप पहले एक पर इंतजार कर रहे हैं, जबकि दूसरा पहले ही समाप्त हो चुका है (वैसे भी 2 को अगले पुनरावृत्ति पर उठाया जाएगा)। यह वही दृष्टिकोण है जिसका आप C में प्रतीक्षा (2) के साथ उपयोग करेंगे।
लुका टेटामांति

7
आह, मैं देख रहा हूँ - अलग व्याख्या :) मैंने प्रश्न को अर्थ के रूप में पढ़ा " जब सबप्रोसेस से बाहर निकलें तो तुरंत कोड 1 वापस करें"।
अलनीतक

56
PID का वास्तव में पुन: उपयोग किया जा सकता है, लेकिन आप ऐसी प्रक्रिया का इंतजार नहीं कर सकते जो वर्तमान प्रक्रिया का बच्चा नहीं है (प्रतीक्षा उस स्थिति में विफल हो जाती है)।
tkokoszka

12
आप n n को संदर्भित करने के लिए% n का उपयोग भी कर सकते हैं: सबसे हाल ही में संदर्भित करने के लिए वें पृष्ठभूमि वाली नौकरी, और %%।
conny

30
@ नील_एम: आप सही हैं, मुझे क्षमा करें। तो यह कुछ इस तरह होगा: for i in $n_procs; do ./procs[${i}] & ; pids[${i}]=$!; done; for pid in ${pids[*]}; do wait $pid; done;सही है?
सिंक

284

http://jeremy.zawodny.com/blog/archives/010717.html :

#!/bin/bash

FAIL=0

echo "starting"

./sleeper 2 0 &
./sleeper 2 1 &
./sleeper 3 0 &
./sleeper 2 0 &

for job in `jobs -p`
do
echo $job
    wait $job || let "FAIL+=1"
done

echo $FAIL

if [ "$FAIL" == "0" ];
then
echo "YAY!"
else
echo "FAIL! ($FAIL)"
fi

103
jobs -pनिष्पादन की स्थिति में आने वाले उपप्रकारों के पीआईडी ​​दे रहा है। यह प्रक्रिया को छोड़ देगा यदि प्रक्रिया पूरी होने से पहले jobs -pही समाप्त हो जाए । इसलिए यदि कोई भी उपप्रकार पहले समाप्त हो जाता है jobs -p, तो उस प्रक्रिया की निकास स्थिति खो जाएगी।
tkokoszka 15

15
वाह, यह उत्तर शीर्ष रेटेड एक की तुलना में बेहतर है। : /
e40

4
@ e40 और नीचे दिया गया उत्तर शायद और भी बेहतर है। और इससे भी बेहतर होगा कि प्रत्येक कमांड को 'cmd; इको "$;" >> "$ tmpfile") के साथ चलाया जाए, इस प्रतीक्षा का उपयोग करें, और फिर विफलताओं के लिए फ़ाइल पढ़ें। साथ ही एनोटेट-आउटपुट। ... या बस इस स्क्रिप्ट का उपयोग करें जब आप इतना परवाह नहीं करते हैं।
होवरहेल

मैं यह जोड़ना चाहता हूं कि यह उत्तर स्वीकार किए जाने से बेहतर है
22

2
@tkokoszka सही होने की jobs -pन जताए PIDs subprocesses की, लेकिन इसके बजाय GPIDs । प्रतीक्षा करने वाला तर्क वैसे भी काम करता है, यह हमेशा समूह पर प्रतीक्षा करता है यदि ऐसा समूह मौजूद है और नहीं तो पीआईडी, लेकिन यह जागरूक होना अच्छा है .. खासकर यदि कोई इस पर निर्माण करना और उपप्रकार को संदेश भेजने जैसे कुछ को शामिल करना था जिसमें मामले वाक्य रचना आप PIDs या GPIDs .. यानी है जो इस पर निर्भर अलग है kill -- -$GPIDबनामkill $PID
टीमो

58

यहाँ सरल उदाहरण का उपयोग कर रहा है wait

कुछ प्रक्रियाएँ चलाएँ:

$ sleep 10 &
$ sleep 10 &
$ sleep 20 &
$ sleep 20 &

फिर उनके साथ waitकमांड के लिए प्रतीक्षा करें :

$ wait < <(jobs -p)

या सिर्फ wait(तर्कों के बिना) सभी के लिए।

यह पृष्ठभूमि में सभी नौकरियों के पूरा होने की प्रतीक्षा करेगा।

यदि -nविकल्प की आपूर्ति की जाती है, तो अगली नौकरी समाप्त होने की प्रतीक्षा करता है और अपनी निकास स्थिति को वापस कर देता है।

देखें: help waitऔर help jobsसिंटैक्स के लिए।

हालांकि नकारात्मक पक्ष यह है कि यह केवल अंतिम आईडी की स्थिति पर लौटेगा, इसलिए आपको प्रत्येक उपप्रकार के लिए स्थिति की जांच करने और उसे चर में संग्रहीत करने की आवश्यकता है।

या विफलता पर कुछ फ़ाइल बनाने के लिए अपनी गणना फ़ंक्शन करें (खाली या असफल लॉग के साथ), फिर उस फ़ाइल की जांच करें यदि मौजूद है, उदाहरण के लिए

$ sleep 20 && true || tee fail &
$ sleep 20 && false || tee fail &
$ wait < <(jobs -p)
$ test -f fail && echo Calculation failed.

1
नए लोगों को कोसने के लिए, यहाँ उदाहरण में दो गणनाएँ हैं sleep 20 && trueऔर sleep 20 && false- (:: अपने फ़ंक्शन के साथ उन्हें बदलें)। समझने &&और ||चलाने के लिए, man bashऔर '/' (खोज) टाइप करें, फिर '^ * लिस्ट' (एक रेगेक्स) फिर दर्ज करें: आदमी नीचे वर्णन करेगा &&और||
drevicko

1
आपको शायद यह देखना चाहिए कि फ़ाइल 'विफल' मौजूद नहीं है (या इसे हटा दें)। आवेदन के आधार पर, ||STDERR को असफल होने के साथ-साथ पकड़ने से पहले '2> & 1' जोड़ना भी एक अच्छा विचार हो सकता है ।
drevicko

मुझे यह पसंद है, कोई कमियां? वास्तव में, केवल जब मैं सभी उपप्रकारों को सूचीबद्ध करना चाहता हूं और कुछ क्रियाएं करना चाहता हूं, जैसे। संकेत भेजें, कि मैं बहीखाता-फ़साद या पुनरावृत्त नौकरियों की कोशिश करूँगा। खत्म होने की प्रतीक्षा करें, बसwait
xgwang

यह नौकरी से बाहर निकलने की स्थिति को याद करेगा जो नौकरियों से पहले विफल हो गया है
-पीआर

50

यदि आपके पास जीएनयू समानांतर स्थापित है, तो आप कर सकते हैं:

# If doCalculations is a function
export -f doCalculations
seq 0 9 | parallel doCalculations {}

जीएनयू समानांतर आपको निकास कोड देगा:

  • 0 - सभी नौकरियां बिना किसी त्रुटि के चलीं।

  • 1-253 - कुछ नौकरियां विफल रहीं। बाहर निकलने की स्थिति विफल नौकरियों की संख्या देती है

  • 254 - 253 से अधिक नौकरियां विफल रहीं।

  • 255 - अन्य त्रुटि।

अधिक जानने के लिए इंट्रो वीडियो देखें: http://pi.dk/1


1
धन्यवाद! लेकिन आप "भ्रम" मुद्दे का उल्लेख करना भूल गए, जो मैं बाद में गिर गया: unix.stackexchange.com/a/35953
nobar

1
यह एक महान उपकरण की तरह दिखता है, लेकिन मुझे नहीं लगता कि उपरोक्त कार्य बश लिपि में है जहां doCalculationsएक समारोह उसी लिपि में परिभाषित किया गया है (हालांकि ओपी इस आवश्यकता के बारे में स्पष्ट नहीं था)। जब मैं कोशिश करता हूं, तो parallelकहता है /bin/bash: doCalculations: command not found(यह seq 0 9ऊपर के उदाहरण के लिए यह 10 बार कहता है)। वर्कअराउंड के लिए यहां देखें ।
नोबार

3
ब्याज की भी: विकल्प के xargsमाध्यम से समानांतर में नौकरियों को लॉन्च करने की कुछ क्षमता है -P। से यहाँ : export -f doCalculations ; seq 0 9 |xargs -P 0 -n 1 -I{} bash -c "doCalculations {}"। के xargsलिए आदमी पृष्ठ में सीमाएं सीमित हैं parallel
नोबार

और अगर doCalculationsकिसी अन्य स्क्रिप्ट-आंतरिक पर्यावरण चर (कस्टम PATH, आदि) पर निर्भर करता है , तो शायद उन्हें exportलॉन्च करने से पहले स्पष्ट रूप से एड होना चाहिए parallel
नोबार

4
@nobar भ्रम कुछ packagers अपने उपयोगकर्ताओं के लिए चीजों को गड़बड़ाने के कारण है। यदि आप उपयोग करते हैं wget -O - pi.dk/3 | shतो आपको कोई भ्रम नहीं होगा। यदि आपके पैकेट ने आपके लिए चीजों को गड़बड़ कर दिया है, तो मैं आपको अपने पैकेट के साथ इस मुद्दे को उठाने के लिए प्रोत्साहित करता हूं। जीएनयू समानांतर के लिए चर और कार्यों को निर्यात किया जाना चाहिए (निर्यात-एफ) उन्हें देखने के लिए (देखें man parallel: gnu.org/software/parallel/… )
Ole Tange

46

कैसे के बारे में बस:

#!/bin/bash

pids=""

for i in `seq 0 9`; do
   doCalculations $i &
   pids="$pids $!"
done

wait $pids

...code continued here ...

अपडेट करें:

जैसा कि कई टिप्पणीकारों द्वारा कहा गया है, उपरोक्त सभी प्रक्रियाओं को जारी रखने से पहले पूरा होने की प्रतीक्षा करता है, लेकिन बाहर नहीं निकलता है और विफल रहता है यदि उनमें से एक विफल रहता है, तो इसे @Bryan, @SamBrightman, और अन्य द्वारा सुझाए गए निम्न संशोधन के साथ किया जा सकता है :

#!/bin/bash

pids=""
RESULT=0


for i in `seq 0 9`; do
   doCalculations $i &
   pids="$pids $!"
done

for pid in $pids; do
    wait $pid || let "RESULT=1"
done

if [ "$RESULT" == "1" ];
    then
       exit 1
fi

...code continued here ...

1
प्रतीक्षा पृष्ठों के अनुसार, कई पीआईडी ​​के साथ प्रतीक्षा करें केवल अंतिम प्रक्रिया के रिटर्न मूल्य का इंतजार करता है। तो आपको एक अतिरिक्त लूप की आवश्यकता है और प्रत्येक पीआईडी ​​के लिए अलग से प्रतीक्षा करें, जैसा कि स्वीकृत उत्तर (टिप्पणियों में) में सुझाया गया है।
व्लाद फ्रोलोव

1
क्योंकि यह इस पृष्ठ पर कहीं और नहीं बताया गया है, मैं जोड़ता हूँ कि लूप होगाfor pid in $pids; do wait $pid; done
ब्रायन

1
@bisounours_tronconneuse हाँ, आप करते हैं। देखें help wait- मल्टीपल आईडी के साथ waitपिछले एक का एक्जिट कोड वापस मिलता है, जैसा कि @ vlad-frolov ने कहा था।
सैम ब्राइटमैन

1
ब्रायन, @SBBrightman ठीक है। मैंने इसे आपके पुनरीक्षण के साथ संशोधित किया है।
patapouf_ai

4
मुझे इस समाधान के साथ एक स्पष्ट चिंता थी: क्या होगा यदि किसी दिए गए प्रक्रिया को संबंधित waitकहा जाने से पहले बाहर निकल जाए ? यह पता चला है कि यह कोई समस्या नहीं है: यदि आप waitपहले से ही बाहर निकलने वाली प्रक्रिया पर हैं, waitतो पहले से ही बाहर निकलने वाली प्रक्रिया की स्थिति से तुरंत बाहर निकल जाएंगे। ( bash
साभार

39

यहाँ मैं अब तक के साथ आया हूँ। मैं यह देखना चाहूंगा कि यदि बच्चा समाप्त हो जाता है तो नींद की आज्ञा को कैसे बाधित किया जाए, ताकि किसी को WAITALL_DELAYउसके उपयोग के लिए ट्यून न करना पड़े ।

waitall() { # PID...
  ## Wait for children to exit and indicate whether all exited with 0 status.
  local errors=0
  while :; do
    debug "Processes remaining: $*"
    for pid in "$@"; do
      shift
      if kill -0 "$pid" 2>/dev/null; then
        debug "$pid is still alive."
        set -- "$@" "$pid"
      elif wait "$pid"; then
        debug "$pid exited with zero exit status."
      else
        debug "$pid exited with non-zero exit status."
        ((++errors))
      fi
    done
    (("$#" > 0)) || break
    # TODO: how to interrupt this sleep when a child terminates?
    sleep ${WAITALL_DELAY:-1}
   done
  ((errors == 0))
}

debug() { echo "DEBUG: $*" >&2; }

pids=""
for t in 3 5 4; do 
  sleep "$t" &
  pids="$pids $!"
done
waitall $pids

संभवतया कोई भी इसे छोड़ सकता है कि WAITALL_DELAY या इसे बहुत कम सेट करें, क्योंकि लूप के अंदर कोई प्रक्रिया शुरू नहीं होती है मुझे नहीं लगता कि यह बहुत महंगा है।
मरिअन

21

इसे समानांतर करने के लिए ...

for i in $(whatever_list) ; do
   do_something $i
done

इसका अनुवाद इस ...

for i in $(whatever_list) ; do echo $i ; done | ## execute in parallel...
   (
   export -f do_something ## export functions (if needed)
   export PATH ## export any variables that are required
   xargs -I{} --max-procs 0 bash -c ' ## process in batches...
      {
      echo "processing {}" ## optional
      do_something {}
      }' 
   )
  • यदि एक प्रक्रिया में कोई त्रुटि होती है, तो यह अन्य प्रक्रियाओं को बाधित नहीं करेगी, लेकिन इसके परिणामस्वरूप अनुक्रम से गैर-शून्य निकास कोड होगा
  • किसी विशेष मामले में निर्यात कार्य और चर आवश्यक नहीं भी हो सकते हैं।
  • आप यह निर्धारित कर सकते --max-procsहैं कि आप कितनी समानता चाहते हैं ( 0मतलब "एक बार में सभी")।
  • जीएनयू समानांतर कुछ अतिरिक्त सुविधाएँ प्रदान करता है जब इसका उपयोग किया जाता है xargs- लेकिन यह हमेशा डिफ़ॉल्ट रूप से स्थापित नहीं होता है।
  • forपाश के बाद से इस उदाहरण में सख्ती से आवश्यक नहीं है echo $iमूल रूप से बस के उत्पादन में पुनः है $(whatever_list)। मुझे लगता है के उपयोग के forकीवर्ड यह थोड़ा आसान देखने के लिए क्या हो रहा है बनाता है।
  • बैश स्ट्रिंग हैंडलिंग भ्रमित कर सकती है - मैंने पाया है कि एकल उद्धरणों का उपयोग करना गैर-तुच्छ लिपियों को लपेटने के लिए सबसे अच्छा काम करता है।
  • आप बैश समानता के अधिक प्रत्यक्ष दृष्टिकोण के विपरीत , पूरे ऑपरेशन (^ C या समान का उपयोग करके) को आसानी से बाधित कर सकते हैं ।

यहाँ एक सरल काम उदाहरण है ...

for i in {0..5} ; do echo $i ; done |xargs -I{} --max-procs 2 bash -c '
   {
   echo sleep {}
   sleep 2s
   }'


7

मुझे विश्वास नहीं है कि यह बैश की अंतर्निहित कार्यक्षमता के साथ संभव है।

आप कर सकते हैं जब एक बच्चे को बाहर निकालता है सूचना मिलती है:

#!/bin/sh
set -o monitor        # enable script job control
trap 'echo "child died"' CHLD

हालाँकि सिग्नल हैंडलर में बच्चे के निकलने की स्थिति प्राप्त करने का कोई स्पष्ट तरीका नहीं है।

उस बच्चे की स्थिति प्राप्त करना आमतौर पर waitनिचले स्तर के पॉसिक्स एपीआई में कार्यों के परिवार का काम है। दुर्भाग्य से इसके लिए बैश का समर्थन सीमित है - आप एक विशिष्ट बच्चे की प्रक्रिया के लिए इंतजार कर सकते हैं (और इसकी निकास स्थिति प्राप्त कर सकते हैं) या आप सभी के लिए इंतजार कर सकते हैं के हैं, और हमेशा 0 परिणाम प्राप्त ।

जो करना असंभव प्रतीत होता है, वह उसके बराबर है waitpid(-1), जो किसी भी बच्चे की प्रक्रिया के लौटने तक अवरुद्ध हो जाता है।


7

मुझे यहाँ पर बहुत सारे अच्छे उदाहरण मिलते हैं, जो मुझे भी फेंक देना चाहते थे।

#! /bin/bash

items="1 2 3 4 5 6"
pids=""

for item in $items; do
    sleep $item &
    pids+="$! "
done

for pid in $pids; do
    wait $pid
    if [ $? -eq 0 ]; then
        echo "SUCCESS - Job $pid exited with a status of $?"
    else
        echo "FAILED - Job $pid exited with a status of $?"
    fi
done

मैं समानांतर में सर्वर / सेवाओं को शुरू / बंद करने के लिए कुछ समान का उपयोग करता हूं और प्रत्येक निकास स्थिति की जांच करता हूं। मेरे लिए बहुत अच्छा काम करता है। आशा है कि यह किसी को मदद करता है!


जब मैं इसे Ctrl + CI के साथ बंद करता हूं तब भी पृष्ठभूमि में चलने वाली प्रक्रियाओं को देखता है।
कार्स्ट जूल

2
@ कर्स्टन - यह एक अलग समस्या है। मान लें कि आप bash का उपयोग कर रहे हैं, तो आप एक एक्जिट कंडीशन (Ctrl + C सहित) को फंसा सकते हैं और वर्तमान में उपयोग की जाने वाली सभी बाल प्रक्रियाओं को मार सकते हैंtrap "kill 0" EXIT
Phil

@Phil सही है। चूंकि ये पृष्ठभूमि प्रक्रियाएं हैं, मूल प्रक्रिया को मारना बस चलने वाली किसी भी बच्चे की प्रक्रिया को छोड़ देता है। मेरा उदाहरण किसी भी संकेत को नहीं फँसाता है, जिसे यदि आवश्यक हो तो फिल कहा जा सकता है।
बजे जेसन स्लोबोट्सकी

6

यह कुछ ऐसा है जो मैं उपयोग करता हूं:

#wait for jobs
for job in `jobs -p`; do wait ${job}; done

5

निम्न कोड सभी गणनाओं को पूरा करने के लिए प्रतीक्षा करेगा और यदि कोई डॉक्यूलेशन विफल रहता है तो निकास स्थिति 1 लौटाएं

#!/bin/bash
for i in $(seq 0 9); do
   (doCalculations $i >&2 & wait %1; echo $?) &
done | grep -qv 0 && exit 1

5

बस शेल से परिणाम स्टोर करें, उदाहरण के लिए एक फ़ाइल में।

#!/bin/bash
tmp=/tmp/results

: > $tmp  #clean the file

for i in `seq 0 9`; do
  (doCalculations $i; echo $i:$?>>$tmp)&
done      #iterate

wait      #wait until all ready

sort $tmp | grep -v ':0'  #... handle as required

5

यहां मेरा संस्करण है जो कई पीड्स के लिए काम करता है, अगर चेतावनी दी जाती है, तो निष्पादन में बहुत लंबा समय लगता है, और यदि निष्पादन किसी दिए गए मूल्य से अधिक समय लेता है, तो उपप्रकार बंद कर देता है।

function WaitForTaskCompletion {
    local pids="${1}" # pids to wait for, separated by semi-colon
    local soft_max_time="${2}" # If execution takes longer than $soft_max_time seconds, will log a warning, unless $soft_max_time equals 0.
    local hard_max_time="${3}" # If execution takes longer than $hard_max_time seconds, will stop execution, unless $hard_max_time equals 0.
    local caller_name="${4}" # Who called this function
    local exit_on_error="${5:-false}" # Should the function exit program on subprocess errors       

    Logger "${FUNCNAME[0]} called by [$caller_name]."

    local soft_alert=0 # Does a soft alert need to be triggered, if yes, send an alert once 
    local log_ttime=0 # local time instance for comparaison

    local seconds_begin=$SECONDS # Seconds since the beginning of the script
    local exec_time=0 # Seconds since the beginning of this function

    local retval=0 # return value of monitored pid process
    local errorcount=0 # Number of pids that finished with errors

    local pidCount # number of given pids

    IFS=';' read -a pidsArray <<< "$pids"
    pidCount=${#pidsArray[@]}

    while [ ${#pidsArray[@]} -gt 0 ]; do
        newPidsArray=()
        for pid in "${pidsArray[@]}"; do
            if kill -0 $pid > /dev/null 2>&1; then
                newPidsArray+=($pid)
            else
                wait $pid
                result=$?
                if [ $result -ne 0 ]; then
                    errorcount=$((errorcount+1))
                    Logger "${FUNCNAME[0]} called by [$caller_name] finished monitoring [$pid] with exitcode [$result]."
                fi
            fi
        done

        ## Log a standby message every hour
        exec_time=$(($SECONDS - $seconds_begin))
        if [ $((($exec_time + 1) % 3600)) -eq 0 ]; then
            if [ $log_ttime -ne $exec_time ]; then
                log_ttime=$exec_time
                Logger "Current tasks still running with pids [${pidsArray[@]}]."
            fi
        fi

        if [ $exec_time -gt $soft_max_time ]; then
            if [ $soft_alert -eq 0 ] && [ $soft_max_time -ne 0 ]; then
                Logger "Max soft execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]."
                soft_alert=1
                SendAlert

            fi
            if [ $exec_time -gt $hard_max_time ] && [ $hard_max_time -ne 0 ]; then
                Logger "Max hard execution time exceeded for task [$caller_name] with pids [${pidsArray[@]}]. Stopping task execution."
                kill -SIGTERM $pid
                if [ $? == 0 ]; then
                    Logger "Task stopped successfully"
                else
                    errrorcount=$((errorcount+1))
                fi
            fi
        fi

        pidsArray=("${newPidsArray[@]}")
        sleep 1
    done

    Logger "${FUNCNAME[0]} ended for [$caller_name] using [$pidCount] subprocesses with [$errorcount] errors."
    if [ $exit_on_error == true ] && [ $errorcount -gt 0 ]; then
        Logger "Stopping execution."
        exit 1337
    else
        return $errorcount
    fi
}

# Just a plain stupid logging function to replace with yours
function Logger {
    local value="${1}"

    echo $value
}

उदाहरण के लिए, सभी तीन प्रक्रियाओं को समाप्त करने के लिए प्रतीक्षा करें, एक चेतावनी लॉग करें यदि निष्पादन 5 सेकंड से अधिक समय तक ले जाता है, तो सभी प्रक्रियाओं को रोक दें यदि निष्पादन में 120 सेकंड से अधिक समय लगता है। विफलताओं पर बाहर निकलने का कार्यक्रम न करें।

function something {

    sleep 10 &
    pids="$!"
    sleep 12 &
    pids="$pids;$!"
    sleep 9 &
    pids="$pids;$!"

    WaitForTaskCompletion $pids 5 120 ${FUNCNAME[0]} false
}
# Launch the function
someting

4

यदि आपके पास 4.2 है या बाद में उपलब्ध है तो निम्नलिखित आपके लिए उपयोगी हो सकता है। यह कार्य नाम और उनके "कोड" के साथ-साथ कार्य के नाम और उनके pids को संग्रहीत करने के लिए साहचर्य सरणियों का उपयोग करता है। मैंने एक सरल दर-सीमित विधि में भी निर्माण किया है जो आपके कार्यों में बहुत अधिक सीपीयू या I / O समय का उपभोग करने पर काम में आ सकती है और आप समवर्ती कार्यों की संख्या को सीमित करना चाहते हैं।

स्क्रिप्ट पहले लूप में सभी कार्यों को लॉन्च करती है और दूसरे में परिणाम खाती है।

यह साधारण मामलों के लिए थोड़ा अधिक है, लेकिन यह बहुत साफ सामान के लिए अनुमति देता है। उदाहरण के लिए, प्रत्येक कार्य को किसी अन्य साहचर्य सारणी में त्रुटि संदेश संग्रहीत कर सकते हैं और सब कुछ शांत हो जाने के बाद उन्हें प्रिंट कर सकते हैं।

#! /bin/bash

main () {
    local -A pids=()
    local -A tasks=([task1]="echo 1"
                    [task2]="echo 2"
                    [task3]="echo 3"
                    [task4]="false"
                    [task5]="echo 5"
                    [task6]="false")
    local max_concurrent_tasks=2

    for key in "${!tasks[@]}"; do
        while [ $(jobs 2>&1 | grep -c Running) -ge "$max_concurrent_tasks" ]; do
            sleep 1 # gnu sleep allows floating point here...
        done
        ${tasks[$key]} &
        pids+=(["$key"]="$!")
    done

    errors=0
    for key in "${!tasks[@]}"; do
        pid=${pids[$key]}
        local cur_ret=0
        if [ -z "$pid" ]; then
            echo "No Job ID known for the $key process" # should never happen
            cur_ret=1
        else
            wait $pid
            cur_ret=$?
        fi
        if [ "$cur_ret" -ne 0 ]; then
            errors=$(($errors + 1))
            echo "$key (${tasks[$key]}) failed."
        fi
    done

    return $errors
}

main

4

मैं बस एक स्क्रिप्ट को पृष्ठभूमि में संशोधित कर रहा हूं और एक प्रक्रिया को समानांतर कर रहा हूं।

मैंने कुछ प्रयोग किया (बैश और ksh दोनों के साथ सोलारिस पर) और पता चला कि 'प्रतीक्षा' शून्य स्थिति से बाहर निकलने की स्थिति का उत्पादन करती है, या यह उन नौकरियों की सूची है जो बिना पीआईडी ​​तर्क प्रदान किए गैर-शून्य बाहर निकलते हैं। उदाहरण के लिए

दे घुमा के:

$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]-  Exit 2                  sleep 20 && exit 2
[2]+  Exit 1                  sleep 10 && exit 1

क्ष:

$ sleep 20 && exit 1 &
$ sleep 10 && exit 2 &
$ wait
[1]+  Done(2)                  sleep 20 && exit 2
[2]+  Done(1)                  sleep 10 && exit 1

यह आउटपुट stderr को लिखा जाता है, इसलिए OPs उदाहरण का एक सरल समाधान हो सकता है:

#!/bin/bash

trap "rm -f /tmp/x.$$" EXIT

for i in `seq 0 9`; do
  doCalculations $i &
done

wait 2> /tmp/x.$$
if [ `wc -l /tmp/x.$$` -gt 0 ] ; then
  exit 1
fi

जबकि यह:

wait 2> >(wc -l)

एक गिनती भी वापस करेगा लेकिन tmp फ़ाइल के बिना। यह भी इस तरह इस्तेमाल किया जा सकता है, उदाहरण के लिए:

wait 2> >(if [ `wc -l` -gt 0 ] ; then echo "ERROR"; fi)

लेकिन यह tmp फ़ाइल IMO की तुलना में बहुत अधिक उपयोगी नहीं है। मैं tmp फ़ाइल से बचने के लिए एक उपयोगी तरीका नहीं खोज सका, जबकि सबस्क्रिप्शन में "प्रतीक्षा" को चलाने से भी बचा जा सकता है, जो बिल्कुल भी काम नहीं करेगा।


3

मैंने इस पर एक जाना है और अन्य उदाहरणों से सभी बेहतरीन हिस्सों को यहां जोड़ा है। यह स्क्रिप्ट checkpidsफ़ंक्शन को निष्पादित करेगी जब कोई भी पृष्ठभूमि प्रक्रिया बाहर निकलती है, और मतदान का सहारा लिए बिना बाहर निकलने की स्थिति का उत्पादन करती है।

#!/bin/bash

set -o monitor

sleep 2 &
sleep 4 && exit 1 &
sleep 6 &

pids=`jobs -p`

checkpids() {
    for pid in $pids; do
        if kill -0 $pid 2>/dev/null; then
            echo $pid is still alive.
        elif wait $pid; then
            echo $pid exited with zero exit status.
        else
            echo $pid exited with non-zero exit status.
        fi
    done
    echo
}

trap checkpids CHLD

wait

3
#!/bin/bash
set -m
for i in `seq 0 9`; do
  doCalculations $i &
done
while fg; do true; done
  • set -m आपको स्क्रिप्ट में fg & bg का उपयोग करने की अनुमति देता है
  • fg, अंतिम प्रक्रिया को अग्रभूमि में रखने के अलावा, इस प्रक्रिया के समान निकास की स्थिति है
  • while fgजब कोई fgशून्य-शून्य निकास स्थिति के साथ बाहर निकलता है तो लूप करना बंद कर देगा

दुर्भाग्य से यह उस स्थिति को संभाल नहीं सकता है जब पृष्ठभूमि में एक प्रक्रिया गैर-शून्य निकास स्थिति के साथ बाहर निकलती है। (लूप तुरंत समाप्त नहीं होगा। यह पिछली प्रक्रियाओं के पूरा होने की प्रतीक्षा करेगा।)


3

यहां पहले से ही बहुत सारे उत्तर हैं, लेकिन मुझे आश्चर्य है कि किसी ने भी सरणियों का उपयोग करने का सुझाव नहीं दिया है ... इसलिए यहां मैंने क्या किया - यह भविष्य में कुछ के लिए उपयोगी हो सकता है।

n=10 # run 10 jobs
c=0
PIDS=()

while true

    my_function_or_command &
    PID=$!
    echo "Launched job as PID=$PID"
    PIDS+=($PID)

    (( c+=1 ))

    # required to prevent any exit due to error
    # caused by additional commands run which you
    # may add when modifying this example
    true

do

    if (( c < n ))
    then
        continue
    else
        break
    fi
done 


# collect launched jobs

for pid in "${PIDS[@]}"
do
    wait $pid || echo "failed job PID=$pid"
done

3

यह काम करता है, @ HoverHell के उत्तर से बेहतर नहीं होने के रूप में बस एक अच्छा होना चाहिए!

#!/usr/bin/env bash

set -m # allow for job control
EXIT_CODE=0;  # exit code of overall script

function foo() {
     echo "CHLD exit code is $1"
     echo "CHLD pid is $2"
     echo $(jobs -l)

     for job in `jobs -p`; do
         echo "PID => ${job}"
         wait ${job} ||  echo "At least one test failed with exit code => $?" ; EXIT_CODE=1
     done
}

trap 'foo $? $$' CHLD

DIRN=$(dirname "$0");

commands=(
    "{ echo "foo" && exit 4; }"
    "{ echo "bar" && exit 3; }"
    "{ echo "baz" && exit 5; }"
)

clen=`expr "${#commands[@]}" - 1` # get length of commands - 1

for i in `seq 0 "$clen"`; do
    (echo "${commands[$i]}" | bash) &   # run the command via bash in subshell
    echo "$i ith command has been issued as a background job"
done

# wait for all to finish
wait;

echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"

# end

और निश्चित रूप से, मैंने इस स्क्रिप्ट को एक एनपीएम परियोजना में अमर कर दिया है, जो आपको समानांतर में बैश कमांड चलाने की अनुमति देती है:

https://github.com/ORESoftware/generic-subshell


trap $? $$लगता है कि
एग्ज़िट

आप इसके बारे में पूरी तरह से निश्चित हैं? यकीन नहीं है कि अगर समझ में आता है।
अलेक्जेंडर मिल्स

2

जाल तुम्हारा दोस्त है आप बहुत सारे सिस्टम में ईआरआर पर फंस सकते हैं। आप प्रत्येक आदेश के बाद कोड का एक टुकड़ा करने के लिए EXIT, या DEBUG पर ट्रैप कर सकते हैं।

यह सभी मानक संकेतों के अतिरिक्त है।


1
कृपया आप कुछ उदाहरणों के साथ अपना उत्तर विस्तृत कर सकते हैं।
19ο atMεδιϲ

2
set -e
fail () {
    touch .failure
}
expect () {
    wait
    if [ -f .failure ]; then
        rm -f .failure
        exit 1
    fi
}

sleep 2 || fail &
sleep 2 && false || fail &
sleep 2 || fail
expect

set -eशीर्ष पर विफलता पर अपनी स्क्रिप्ट बंद कर देता है।

expect1यदि कोई सबजोब विफल हो गया तो वापस आ जाएगा ।


2

इस उद्देश्य के लिए मैंने एक bashफ़ंक्शन लिखा था :for

नोट : :forन केवल फ़ंक्शन के निकास कोड को सुरक्षित रखता है और लौटाता है, बल्कि सभी समानांतर चल रहे उदाहरणों को भी समाप्त करता है। इस मामले में जिसकी आवश्यकता नहीं हो सकती है।

#!/usr/bin/env bash

# Wait for pids to terminate. If one pid exits with
# a non zero exit code, send the TERM signal to all
# processes and retain that exit code
#
# usage:
# :wait 123 32
function :wait(){
    local pids=("$@")
    [ ${#pids} -eq 0 ] && return $?

    trap 'kill -INT "${pids[@]}" &>/dev/null || true; trap - INT' INT
    trap 'kill -TERM "${pids[@]}" &>/dev/null || true; trap - RETURN TERM' RETURN TERM

    for pid in "${pids[@]}"; do
        wait "${pid}" || return $?
    done

    trap - INT RETURN TERM
}

# Run a function in parallel for each argument.
# Stop all instances if one exits with a non zero
# exit code
#
# usage:
# :for func 1 2 3
#
# env:
# FOR_PARALLEL: Max functions running in parallel
function :for(){
    local f="${1}" && shift

    local i=0
    local pids=()
    for arg in "$@"; do
        ( ${f} "${arg}" ) &
        pids+=("$!")
        if [ ! -z ${FOR_PARALLEL+x} ]; then
            (( i=(i+1)%${FOR_PARALLEL} ))
            if (( i==0 )) ;then
                :wait "${pids[@]}" || return $?
                pids=()
            fi
        fi
    done && [ ${#pids} -eq 0 ] || :wait "${pids[@]}" || return $?
}

प्रयोग

for.sh:

#!/usr/bin/env bash
set -e

# import :for from gist: https://gist.github.com/Enteee/c8c11d46a95568be4d331ba58a702b62#file-for
# if you don't like curl imports, source the actual file here.
source <(curl -Ls https://gist.githubusercontent.com/Enteee/c8c11d46a95568be4d331ba58a702b62/raw/)

msg="You should see this three times"

:(){
  i="${1}" && shift

  echo "${msg}"

  sleep 1
  if   [ "$i" == "1" ]; then sleep 1
  elif [ "$i" == "2" ]; then false
  elif [ "$i" == "3" ]; then
    sleep 3
    echo "You should never see this"
  fi
} && :for : 1 2 3 || exit $?

echo "You should never see this"
$ ./for.sh; echo $?
You should see this three times
You should see this three times
You should see this three times
1

संदर्भ


1

मैंने हाल ही में इसका उपयोग किया (Alnitak के लिए धन्यवाद):

#!/bin/bash
# activate child monitoring
set -o monitor

# locking subprocess
(while true; do sleep 0.001; done) &
pid=$!

# count, and kill when all done
c=0
function kill_on_count() {
    # you could kill on whatever criterion you wish for
    # I just counted to simulate bash's wait with no args
    [ $c -eq 9 ] && kill $pid
    c=$((c+1))
    echo -n '.' # async feedback (but you don't know which one)
}
trap "kill_on_count" CHLD

function save_status() {
    local i=$1;
    local rc=$2;
    # do whatever, and here you know which one stopped
    # but remember, you're called from a subshell
    # so vars have their values at fork time
}

# care must be taken not to spawn more than one child per loop
# e.g don't use `seq 0 9` here!
for i in {0..9}; do
    (doCalculations $i; save_status $i $?) &
done

# wait for locking subprocess to be killed
wait $pid
echo

वहां से कोई भी आसानी से एक्सट्रपलेशन कर सकता है, और उस ट्रिगर का जवाब देने के लिए एक ट्रिगर (एक फाइल को टच करें, एक सिग्नल भेजें) और काउंटिंग मानदंड (टच की गई फाइलें, या जो भी हो) को बदल दें। या यदि आप केवल 'कोई' गैर शून्य आरसी चाहते हैं, तो बस save_status से लॉक को मारें।


1

मुझे इसकी आवश्यकता थी, लेकिन लक्ष्य प्रक्रिया वर्तमान शेल का बच्चा नहीं थी, इस मामले में wait $PIDकाम नहीं करता है। मुझे इसके बजाय निम्नलिखित विकल्प मिला:

while [ -e /proc/$PID ]; do sleep 0.1 ; done

यह procfs की उपस्थिति पर निर्भर करता है , जो उपलब्ध नहीं हो सकता है (मैक इसे उदाहरण के लिए प्रदान नहीं करता है)। पोर्टेबिलिटी के लिए, आप इसके बजाय इसका उपयोग कर सकते हैं:

while ps -p $PID >/dev/null ; do sleep 0.1 ; done

1

CHLD सिग्नल फँसाने से काम नहीं चल सकता क्योंकि आप एक साथ आने पर कुछ सिग्नल खो सकते हैं।

#!/bin/bash

trap 'rm -f $tmpfile' EXIT

tmpfile=$(mktemp)

doCalculations() {
    echo start job $i...
    sleep $((RANDOM % 5)) 
    echo ...end job $i
    exit $((RANDOM % 10))
}

number_of_jobs=10

for i in $( seq 1 $number_of_jobs )
do
    ( trap "echo job$i : exit value : \$? >> $tmpfile" EXIT; doCalculations ) &
done

wait 

i=0
while read res; do
    echo "$res"
    let i++
done < "$tmpfile"

echo $i jobs done !!!

1

कई उपप्रकारों के लिए प्रतीक्षा करने और बाहर निकलने के लिए जब उनमें से कोई भी गैर-शून्य स्थिति कोड के साथ बाहर निकलता है तो 'वेट' का उपयोग करके

#!/bin/bash
wait_for_pids()
{
    for (( i = 1; i <= $#; i++ )) do
        wait -n $@
        status=$?
        echo "received status: "$status
        if [ $status -ne 0 ] && [ $status -ne 127 ]; then
            exit 1
        fi
    done
}

sleep_for_10()
{
    sleep 10
    exit 10
}

sleep_for_20()
{
    sleep 20
}

sleep_for_10 &
pid1=$!

sleep_for_20 &
pid2=$!

wait_for_pids $pid2 $pid1

स्थिति कोड '127' गैर-मौजूदा प्रक्रिया के लिए है जिसका अर्थ है कि बच्चा बाहर हो सकता है।


1

सभी नौकरियों के लिए प्रतीक्षा करें और अंतिम असफल नौकरी के निकास कोड को वापस करें। उपरोक्त समाधानों के विपरीत, इसके लिए पीआईडी ​​बचत की आवश्यकता नहीं होती है। बस bg दूर, और प्रतीक्षा करें।

function wait_ex {
    # this waits for all jobs and returns the exit code of the last failing job
    ecode=0
    while true; do
        wait -n
        err="$?"
        [ "$err" == "127" ] && break
        [ "$err" != "0" ] && ecode="$err"
    done
    return $ecode
}

यह काम करेगा और मज़बूती से आपके निष्पादित कमांड से पहला त्रुटि कोड देगा जब तक कि यह "कमांड नहीं मिला" (कोड 127) हो।
drevicko

0

एक मामला हो सकता है जहां प्रक्रिया की प्रतीक्षा करने से पहले प्रक्रिया पूरी हो जाती है। यदि हम एक ऐसी प्रक्रिया के लिए प्रतीक्षा को ट्रिगर करते हैं जो पहले से ही समाप्त हो गई है, तो यह एक त्रुटि को ट्रिगर करेगा जैसे कि पीआईडी ​​इस शेल का बच्चा नहीं है। ऐसे मामलों से बचने के लिए, निम्नलिखित प्रक्रिया का उपयोग यह जानने के लिए किया जा सकता है कि प्रक्रिया पूरी हो चुकी है या नहीं:

isProcessComplete(){
PID=$1
while [ -e /proc/$PID ]
do
    echo "Process: $PID is still running"
    sleep 5
done
echo "Process $PID has finished"
}

0

मुझे लगता है कि समानांतर में नौकरियों को चलाने और स्थिति की जांच करने का सबसे सीधा आगे का तरीका अस्थायी फाइलों का उपयोग करना है। पहले से ही कुछ ऐसे ही उत्तर हैं (जैसे नीत्शे-जौ और मोग896)।

#!/bin/bash
rm -f fail
for i in `seq 0 9`; do
  doCalculations $i || touch fail &
done
wait 
! [ -f fail ]

उपरोक्त कोड थ्रेड सुरक्षित नहीं है। यदि आप चिंतित हैं कि ऊपर दिया गया कोड उसी समय चल रहा होगा, तो अधिक विशिष्ट फ़ाइल नाम का उपयोग करना बेहतर होगा, जैसे विफल। $$। अंतिम पंक्ति आवश्यकता को पूरा करने के लिए है: "रिटर्न एग्जिट कोड 1 जब कोई भी उपप्रकार कोड के साथ समाप्त होता है! = 0!" मैं वहाँ एक अतिरिक्त आवश्यकता को साफ करने के लिए फेंक दिया। इसे इस तरह लिखना स्पष्ट हो सकता है:

#!/bin/bash
trap 'rm -f fail.$$' EXIT
for i in `seq 0 9`; do
  doCalculations $i || touch fail.$$ &
done
wait 
! [ -f fail.$$ ] 

यहां कई नौकरियों के परिणाम एकत्र करने के लिए एक समान स्निपेट है: मैं एक अस्थायी निर्देशिका बनाता हूं, सभी उप कार्यों के आउटपुट को एक अलग फ़ाइल में कहानी करता हूं, और फिर उन्हें समीक्षा के लिए डंप करता हूं। यह वास्तव में सवाल से मेल नहीं खाता - मैं इसे बोनस के रूप में फेंक रहा हूं:

#!/bin/bash
trap 'rm -fr $WORK' EXIT

WORK=/tmp/$$.work
mkdir -p $WORK
cd $WORK

for i in `seq 0 9`; do
  doCalculations $i >$i.result &
done
wait 
grep $ *  # display the results with filenames and contents

0

मैं लगभग jobs -pपीआईडी ​​इकट्ठा करने के लिए उपयोग करने के जाल में गिर गया , जो काम नहीं करता है यदि बच्चा पहले से ही बाहर निकल चुका है, जैसा कि नीचे दी गई स्क्रिप्ट में दिखाया गया है। मैंने जो समाधान निकाला, वह केवल wait -nएन बार कह रहा था , जहां एन मेरे पास बच्चों की संख्या है, जो मुझे नियत रूप से पता है।

#!/usr/bin/env bash

sleeper() {
    echo "Sleeper $1"
    sleep $2
    echo "Exiting $1"
    return $3
}

start_sleepers() {
    sleeper 1 1 0 &
    sleeper 2 2 $1 &
    sleeper 3 5 0 &
    sleeper 4 6 0 &
    sleep 4
}

echo "Using jobs"
start_sleepers 1

pids=( $(jobs -p) )

echo "PIDS: ${pids[*]}"

for pid in "${pids[@]}"; do
    wait "$pid"
    echo "Exit code $?"
done

echo "Clearing other children"
wait -n; echo "Exit code $?"
wait -n; echo "Exit code $?"

echo "Waiting for N processes"
start_sleepers 2

for ignored in $(seq 1 4); do
    wait -n
    echo "Exit code $?"
done

आउटपुट:

Using jobs
Sleeper 1
Sleeper 2
Sleeper 3
Sleeper 4
Exiting 1
Exiting 2
PIDS: 56496 56497
Exiting 3
Exit code 0
Exiting 4
Exit code 0
Clearing other children
Exit code 0
Exit code 1
Waiting for N processes
Sleeper 1
Sleeper 2
Sleeper 3
Sleeper 4
Exiting 1
Exiting 2
Exit code 0
Exit code 2
Exiting 3
Exit code 0
Exiting 4
Exit code 0
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.