समानांतर पृष्ठभूमि प्रक्रियाओं के उप-कोड इकट्ठा करें (उप गोले)


18

कहो हमारे पास एक बैश स्क्रिप्ट है जैसे:

echo "x" &
echo "y" &
echo "z" &
.....
echo "Z" &
wait

क्या उप-गोले / उप प्रक्रियाओं के निकास कोड को इकट्ठा करने का एक तरीका है? ऐसा करने के लिए रास्ता खोज रहे हैं और कुछ भी नहीं पा सकते हैं। मुझे इन उप-श्रेणियों को समानांतर में चलाने की आवश्यकता है, अन्यथा हां यह आसान होगा।

मैं एक सामान्य समाधान की तलाश में हूं (समानांतर में चलने के लिए मेरे पास अज्ञात / गतिशील संख्या में उप प्रक्रियाएं हैं)।


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

3
मुझे वास्तव में लगता है कि सवाल अब अच्छा है - मेरे पास उप प्रक्रियाओं की एक गतिशील संख्या है। मुझे सभी निकास कोड एकत्र करने की आवश्यकता है। बस इतना ही।
अलेक्जेंडर मिल्स

जवाबों:


6

अलेक्जेंडर मिल्स द्वारा जवाब जो हैंडल का उपयोग करता है, ने मुझे एक शानदार शुरुआत दी, लेकिन मुझे यह त्रुटि भी दी

चेतावनी: run_pending_traps: trap_list में खराब मूल्य [17]: 0x461010

जो बैश जाति-स्थिति की समस्या हो सकती है

इसके बजाय मैंने बस प्रत्येक बच्चे के पिड स्टोर किए और प्रतीक्षा की और विशेष रूप से प्रत्येक बच्चे के लिए एग्जिट कोड प्राप्त किया। मैं इस क्लीनर को उपप्रोसेस स्पॉन्गिंग सबप्रोसेस के रूप में फंक्शंस में देखता हूं और एक पेरेंट प्रक्रिया के इंतजार के जोखिम से बचता हूं, जहां मुझे बच्चे के लिए इंतजार करना था। यह स्पष्ट है कि क्या होता है क्योंकि इसके जाल का उपयोग नहीं किया जाता है।

#!/usr/bin/env bash

# it seems it does not work well if using echo for function return value, and calling inside $() (is a subprocess spawned?) 
function wait_and_get_exit_codes() {
    children=("$@")
    EXIT_CODE=0
    for job in "${children[@]}"; do
       echo "PID => ${job}"
       CODE=0;
       wait ${job} || CODE=$?
       if [[ "${CODE}" != "0" ]]; then
           echo "At least one test failed with exit code => ${CODE}" ;
           EXIT_CODE=1;
       fi
   done
}

DIRN=$(dirname "$0");

commands=(
    "{ echo 'a'; exit 1; }"
    "{ echo 'b'; exit 0; }"
    "{ echo 'c'; exit 2; }"
    )

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

children_pids=()
for i in `seq 0 "$clen"`; do
    (echo "${commands[$i]}" | bash) &   # run the command via bash in subshell
    children_pids+=("$!")
    echo "$i ith command has been issued as a background job"
done
# wait; # wait for all subshells to finish - its still valid to wait for all jobs to finish, before processing any exit-codes if we wanted to
#EXIT_CODE=0;  # exit code of overall script
wait_and_get_exit_codes "${children_pids[@]}"

echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"
# end

शांत, मुझे लगता है कि हालांकि, स्पष्टता के लिए for job in "${childen[@]}"; doहोना चाहिएfor job in "${1}"; do
अलेक्जेंडर मिल्स

इस लिपि के साथ मेरी एकमात्र चिंता यह है कि children_pids+=("$!")क्या वास्तव में सब शेल के लिए वांछित पीड को कैप्चर करना है।
अलेक्जेंडर मिल्स

1
मैंने "$ {1}" के साथ परीक्षण किया और यह काम नहीं करता है। मैं फ़ंक्शन के लिए एक सरणी पास कर रहा हूं, और जाहिर है कि बैश में विशेष ध्यान देने की आवश्यकता है। $! अंतिम स्पॉन्ड जॉब की pid है, tldp.org/LDP/abs/html/internalvariables.html देखें। यह मेरे परीक्षणों में सही ढंग से काम करने लगता है, और मैं अब unraID cache_dirs स्क्रिप्ट में उपयोग कर रहा हूं, और ऐसा लगता है। इसका काम है। मैं bash 4.4.12 का उपयोग कर रहा हूं।
15

अच्छा हां लगता है जैसे आप सही हैं
अलेक्जेंडर मिल्स

20

waitएक पीआईडी ​​के साथ प्रयोग करें , जो होगा:

प्रतीक्षा करें जब तक कि प्रत्येक प्रक्रिया आईडी पिड या जॉब स्पेसिफिकेशन जॉबस्पेक द्वारा निर्दिष्ट बच्चे की प्रक्रिया समाप्त न हो जाए और अंतिम कमांड के एक्जिट स्टेटस का इंतजार करें।

आपके जाते ही आपको प्रत्येक प्रक्रिया के PID को सहेजना होगा:

echo "x" & X=$!
echo "y" & Y=$!
echo "z" & Z=$!

आप स्क्रिप्ट में नौकरी नियंत्रण को सक्षम कर सकते हैं set -mऔर %nजॉबस्पेक का उपयोग कर सकते हैं, लेकिन आप लगभग निश्चित रूप से नहीं करना चाहते हैं - नौकरी पर नियंत्रण के कई अन्य दुष्प्रभाव हैं

waitउसी कोड को वापस करेगा जैसे प्रक्रिया समाप्त हुई। आप wait $Xबाद में अंतिम कोड तक पहुँचने के लिए किसी भी (उचित) बिंदु का उपयोग कर सकते हैं $?या इसे सही / गलत के रूप में उपयोग कर सकते हैं:

echo "x" & X=$!
echo "y" & Y=$!
...
wait $X
echo "job X returned $?"

wait जब तक यह पहले से ही पूरा नहीं हो जाता है तब तक कमांड पूरा हो जाएगा।

आप ऐसा रोकने से बचना चाहते हैं, तो आप कर सकते हैं एक सेट trapपरSIGCHLD , समाप्त की संख्या की गणना, और सभी संभाल waitएक बार जब वे सब समाप्त कर लिया है पर रों। आप waitलगभग हर समय अकेले उपयोग करने से दूर हो सकते हैं।


1
ओह, क्षमा करें, मुझे इन उप-श्रेणियों को समानांतर में चलाने की आवश्यकता है, मैं इस प्रश्न में निर्दिष्ट करूंगा ...
अलेक्जेंडर मिल्स

कोई बात नहीं, शायद यह मेरे सेटअप के साथ काम करता है ... आपके कोड में प्रतीक्षा कमांड कहां से आती है? मैं अनुसरण नहीं करता
अलेक्जेंडर मिल्स

1
@AlexanderMills वे कर रहे हैं साथ में चल रहे। यदि आपके पास उनमें से एक चर संख्या है, तो एक सरणी का उपयोग करें। (जैसे कि यहाँ जो इस प्रकार एक डुप्लिकेट हो सकता है)।
माइकल होमर

हाँ धन्यवाद, मैं इसकी जाँच करूँगा, यदि प्रतीक्षा आदेश आपके उत्तर से संबंधित है, तो कृपया इसे जोड़ें
अलेक्जेंडर मिल्स

आप बाद wait $Xमें किसी भी (उचित) बिंदु पर चलते हैं ।
माइकल होमर

5

यदि आपके पास कमांड्स की पहचान करने का एक अच्छा तरीका है, तो आप उनके एक्जिट कोड को एक tmp फाइल में प्रिंट कर सकते हैं और फिर उस विशिष्ट फ़ाइल तक पहुंच सकते हैं, जिसमें आप रुचि रखते हैं:

#!/bin/bash

for i in `seq 1 5`; do
    ( sleep $i ; echo $? > /tmp/cmd__${i} ) &
done

wait

for i in `seq 1 5`; do # or even /tmp/cmd__*
    echo "process $i:"
    cat /tmp/cmd__${i}
done

Tmp फ़ाइलों को हटाने के लिए मत भूलना।


4

compound commandकोष्ठक में एक कथन का प्रयोग करें :

( echo "x" ; echo X: $? ) &
( true ; echo TRUE: $? ) &
( false ; echo FALSE: $? ) &

आउटपुट देगा

x
X: 0
TRUE: 0
FALSE: 1

जीएनयू समानांतर का उपयोग करके समानांतर में कई कमांड चलाने का एक अलग तरीका है । चलाने और उन्हें फ़ाइल में रखने के लिए आदेशों की एक सूची बनाएँ list:

cat > list
sleep 2 ; exit 7
sleep 3 ; exit 55
^D

समानांतर में सभी कमांड चलाएं और फ़ाइल में निकास कोड एकत्र करें job.log:

cat list | parallel -j0 --joblog job.log
cat job.log

और आउटपुट है:

Seq     Host    Starttime       JobRuntime      Send    Receive Exitval Signal  Command
1       :       1486892487.325       1.976      0       0       7       0       sleep 2 ; exit 7
2       :       1486892487.326       3.003      0       0       55      0       sleep 3 ; exit 55

ठीक है, धन्यवाद, क्या यह उत्पन्न करने का कोई तरीका है? मेरे पास केवल 3 उप प्रक्रियाएँ नहीं हैं, मेरे पास Z उप प्रक्रियाएँ हैं।
अलेक्जेंडर मिल्स

मैंने मूल प्रश्न को प्रतिबिंबित करने के लिए अद्यतन किया कि मैं एक सामान्य समाधान ढूंढ रहा हूं, धन्यवाद
अलेक्जेंडर मिल्स

इसे उत्पन्न करने का एक तरीका एक लूपिंग निर्माण का उपयोग करना हो सकता है?
अलेक्जेंडर मिल्स

लूपिंग? क्या आपके पास आदेशों की एक निश्चित सूची है या क्या वह उपयोगकर्ता द्वारा नियंत्रित है? मुझे यकीन नहीं है कि मैं समझता हूं कि आप क्या करने की कोशिश कर रहे हैं, लेकिन शायद PIPESTATUSकुछ ऐसा है जिसे आपको जांचना चाहिए। यह seq 10 | gzip -c > seq.gz ; echo ${PIPESTATUS[@]}रिटर्न 0 0(पहले और अंतिम आदेश से बाहर निकलें कोड)।
hschou

हाँ उपयोगकर्ता द्वारा अनिवार्य रूप से नियंत्रित किया जाता है
अलेक्जेंडर मिल्स

2

यह सामान्य स्क्रिप्ट है जिसे आप खोज रहे हैं। केवल नकारात्मक पक्ष यह है कि आपके आदेश उद्धरण में हैं, जिसका मतलब है कि आपके आईडीई के माध्यम से हाइलाइटिंग वास्तव में काम नहीं करेगा। अन्यथा, मैंने कुछ अन्य उत्तरों की कोशिश की है और यह सबसे अच्छा है। यह उत्तर wait <pid>@Michael द्वारा दिए गए उपयोग के विचार को शामिल करता है, लेकिन trapसबसे अच्छा काम करने वाले कमांड का उपयोग करके एक कदम आगे जाता है ।

#!/usr/bin/env bash

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

function handleJobs() {
     for job in `jobs -p`; do
         echo "PID => ${job}"
         CODE=0;
         wait ${job} || CODE=$?
         if [[ "${CODE}" != "0" ]]; then
         echo "At least one test failed with exit code => ${CODE}" ;
         EXIT_CODE=1;
         fi
     done
}

trap 'handleJobs' CHLD  # trap command is the key part
DIRN=$(dirname "$0");

commands=(
    "{ echo 'a'; exit 1; }"
    "{ echo 'b'; exit 0; }"
    "{ echo 'c'; exit 2; }"
)

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; # wait for all subshells to finish

echo "EXIT_CODE => $EXIT_CODE"
exit "$EXIT_CODE"
# end

मुझे सही रास्ते पर लाने के लिए @michael होमर को धन्यवाद, लेकिन trapकमांड का उपयोग करना AFAICT के लिए सबसे अच्छा तरीका है।


1
जब आप बाहर निकलते हैं, तो उस समय स्टेटस को प्रिंट करने के लिए आप SIGCHLD जाल का उपयोग कर सकते हैं। या एक प्रगति काउंटर को अपडेट करना: एक फ़ंक्शन घोषित करें फिर "ट्रैप
फंक्शन_नाम CHLD

1
इसके अलावा "वेट-एन" किसी भी बच्चे के लिए इंतजार करेगा और फिर $ में उस बच्चे की निकास स्थिति वापस करेगा? चर। इसलिए आप प्रगति को प्रिंट कर सकते हैं क्योंकि हर एक बाहर निकलता है। हालाँकि, ध्यान दें कि जब तक आप CHLD जाल का उपयोग नहीं करते हैं, तब तक आपको कुछ बच्चे छूट सकते हैं।
चूनको

@ चन्को धन्यवाद! यह अच्छी जानकारी है, क्या आप कुछ ऐसा उत्तर दे सकते हैं जिसे आप सबसे अच्छा मानते हैं?
अलेक्जेंडर मिल्स

धन्यवाद @Chunko, जाल बेहतर काम करता है, तुम सही हो। प्रतीक्षा के साथ <pid>, मैं नीच हो गया।
अलेक्जेंडर मिल्स

क्या आप समझा सकते हैं कि कैसे और क्यों आप मानते हैं कि जाल के साथ संस्करण इसके बिना एक से बेहतर है? (मेरा मानना ​​है कि यह बेहतर नहीं है, और इसलिए यह बदतर है, क्योंकि यह बिना किसी लाभ के अधिक जटिल है।)
स्कॉट

1

@Rolf के उत्तर की एक और विविधता:

बाहर निकलने की स्थिति को बचाने का एक और तरीका कुछ ऐसा होगा

mkdir /tmp/status_dir

और फिर प्रत्येक स्क्रिप्ट है

script_name="${0##*/}"  ## strip path from script name
tmpfile="/tmp/status_dir/${script_name}.$$"
do something
rc=$?
echo "$rc" > "$tmpfile"

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

तुम भी प्रत्येक स्क्रिप्ट से कुछ की तरह कर एक से अधिक स्थिति बचा सकते हैं

tmpfile="$(/bin/mktemp -q "/tmp/status_dir/${script_name}.$$.XXXXXX")"

जो पहले की तरह फ़ाइल बनाता है, लेकिन इसमें एक अद्वितीय यादृच्छिक स्ट्रिंग जोड़ता है।

या, आप एक ही फ़ाइल में अधिक स्थिति जानकारी जोड़ सकते हैं।


1

script3निष्पादित किया जाएगा केवल अगर script1और script2सफल रहे हैं और script1और script2समानांतर में निष्पादित किया जाएगा:

./script1 &
process1=$!

./script2 &
process2=$!

wait $process1
rc1=$?

wait $process2
rc2=$?

if [[ $rc1 -eq 0 ]] && [[ $rc2 -eq 0  ]];then
./script3
fi

AFAICT, यह माइकल होमर के जवाब के पुनर्विचार से ज्यादा कुछ नहीं है ।
स्कॉट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.