सुरक्षित रूप से कई कार्यक्रमों द्वारा मुद्रित लाइनों को "गठबंधन" कैसे करें?


11

मान लीजिए कि मैं समानांतर में कई कार्यक्रमों को निष्पादित करना चाहता हूं और उनके आउटपुट को एक पाइप से जोड़ना चाहता हूं:

sh -c '
    (echo qqq; echo qqq2; echo qqq3)&
    (echo www; echo www2; echo www3)& 
    (echo eee; echo eee2; echo eee3)& 
  wait; wait; wait'

यह शेल दृष्टिकोण इस सरल मामले के लिए अच्छी तरह से काम करता है, लेकिन मुझे उम्मीद है कि यह विफल हो सकता है अगर प्रोग्राम आउटपुट और अधिक लंबी लाइनों को बफर तरीके से आउटपुट करते हैं, जैसे कि यह (निर्माण):

qqq
qqwww
q2
qqq3www2

wwweee3

eee2
eee3

मेरे द्वारा उपयोग किए जाने के संकेत दिए गए समाधान में से एक था tail -f:

tail -n +0 -q -f <(echo qqq; echo qqq2; echo qqq3) <(echo www; echo www2; echo www3) <(echo eee; echo eee2; echo eee3)

, लेकिन यह उप-इष्टतम विकल्प है: यह डेटा को सुस्त रूप से आउटपुट करता है, यह समाप्त नहीं होता है; मैं आउटपुट "स्लीप" क्रम में नहीं देखता, लेकिन इस मामले में तर्क क्रम में:

tail -n +0 -q -f <(sleep 1; echo qqq; sleep 1; echo qqq2; echo qqq3) <(echo www; echo www2; sleep 10; echo www3) <(echo eee; sleep 4; echo eee2; echo eee3) | cat

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

मानक टूल (और बिना tail -fनुकसान के) का उपयोग करके इसे कैसे करें ?


आप आउटपुट कैसे मिक्स करना चाहते हैं? जाहिरा तौर पर आप "तर्क क्रम" के बजाय "स्लीप ऑर्डर" चाहते हैं, इसलिए आप आउटपुट को मिक्स करना चाहते हैं। क्या आपकी आवश्यकता आउटपुट को मिक्स करने की है लेकिन लाइनों की नहीं, यानी प्रत्येक लाइन को एटोमिकली प्रिंट करने की?
गिल्स एसओ- बुराई को रोकना '

Linewise। सभी शुरू किए गए कार्यक्रमों से सभी लाइनों को जल्दी से वितरित किया जाना चाहिए, लेकिन प्रत्येक पंक्ति के अंदर मिश्रण के बिना।
वि।

मुझे लगता है कि ऐसा करने का मानक तरीका कहा जाता है, ठीक है, syslog...
शादुर

syslogलॉग के लिए उपयोग नहीं कर रहा है , लेकिन कुछ कस्टम के लिए ठीक माना जाता है?
वि।

यह अब तक पोस्ट किए गए अन्य सुझावों की तुलना में अधिक आदर्श नहीं है, लेकिन मुझे लगा कि यह -sपूंछ के लिए विकल्प का उल्लेख करने के लायक होगा । उदाहरण के tail -f -s .1 fileलिए, लूप की देरी को 1 सेकंड से 1 सेकंड तक कम कर देगा।
cpugeniusmv

जवाबों:


4

जीएनयू समानांतर।

अगस्त 2013 से जारी नोट्स से :

--line-bufferलाइन के आधार पर बफर आउटपुट देगा। --groupपूरे काम के लिए आउटपुट को एक साथ रखता है। --ungroupएक जॉब से आने वाली आधी लाइन और दूसरी जॉब से आने वाली आधी लाइन के साथ मिक्सअप को आउटपुट देता है। --line-bufferइन दोनों के बीच फिट बैठता है; यह एक पूर्ण रेखा प्रिंट करता है, लेकिन विभिन्न नौकरियों की लाइनों को मिलाने की अनुमति देगा।

उदाहरण के लिए:

parallel --line-buffer <jobs

कहाँ jobsशामिल हैं:

./long.sh
./short.sh one
./short.sh two

short.sh:

#!/bin/bash

while true; do
        echo "short line $1"
        sleep .1
done

long.sh:

#!/bin/bash

count=0
while true; do
        echo -n "long line with multiple write()s "
        sleep .1
        count=$((count+1))
        if [ $count -gt 30 ]; then
                count=0
                echo
        fi
done

आउटपुट:

short line one
short line two
short line one
short line two
short line one
**-snip-**
short line one
short line one
short line two
short line two
short line one
short line one
short line one
long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s long line with multiple write()s 
short line two
short line two
short line two
short line one

1

ताले को लागू करने वाला एक समाधान:

function putlines () {
   read line || return $?
   while ! ln -s $$ lock >/dev/null 2>&1
   do
      sleep 0.05
   done
   echo "$line" 
}

function getlines () {
     while read lline
     do 
          echo "$lline"
          rm lock
     done
}

# your paralelized jobs  
(  
   job1 | putlines & 
   job2 | putlines & 
   job3 | putlines & 
   wait
) | getlines| final_processing

फाइलसिस्टम का उपयोग करने की तुलना में लॉक बनाने का एक तेज़ तरीका होना चाहिए।


0

मैं कुछ भी सरल नहीं सोच सकता, इससे आपको मदद मिलेगी, यदि आपकी लाइनें इतनी लंबी हैं, कि एक कार्यक्रम को सोने से पहले भेजा जाएगा, जो कि एक पंक्ति को लिखने के लिए समाप्त करने में सक्षम था।

हालाँकि, यदि आपकी लाइनें पूरी तरह से प्रोसेस स्विच करने से पहले पूरी तरह से लिखी जा रही हैं, और आपकी समस्या यह है, कि एक लाइन तैयार करने में बहुत लंबा समय लगता है, तो आप पढ़े हुए आउटपुट को बफर कर सकते हैं।

उदाहरण के लिए:

((./script1 | while read line1; do echo $line1; done) & \
(./script2 | while read line2; do echo $line2; done)) | doSomethingWithOutput

सुंदर नहीं। पूरी तरह से विश्वसनीय है। निश्चित रूप से वह प्रदर्शन अच्छा रहेगा।
वि।

आप सही हे। यह सुंदर नहीं है, लेकिन एक गंदे हैक की तरह दिखता है। हालांकि, मुझे नहीं लगता कि प्रदर्शन और विश्वसनीयता को आंकने के लिए यह काफी है। इसके अलावा, आप 'मानक उपकरण' का उपयोग करना चाहते थे। इसलिए मुझे आश्चर्य नहीं होगा, अगर आपको कुछ कुरूपता को स्वीकार करना है (अंत में)। लेकिन शायद किसी के पास अधिक संतोषजनक समाधान है।
xwst 30'13

वर्तमान में मैं अपने कार्यक्रम (प्रश्न में जुड़ा हुआ) से संतुष्ट हूं, सिवाय इसके कि यह रिपॉजिटरी में उपलब्ध नहीं है, इसलिए इसे थोड़ा "मानक" भी नहीं माना जा सकता है। समाधान हो सकता है कि इसे वहां धकेलने की कोशिश हो ...
Vi

0

आप एक नामित पाइप बना सकते हैं mkfifo, सभी आउटपुट को नामित पाइप में डंप कर सकते हैं, और आपके एकत्रित डेटा के लिए नामित पाइप से अलग से पढ़ा जा सकता है:

mkfifo /tmp/mypipe
job1 > /tmp/mypipe &
job2 > /tmp/mypipe &
job3 > /tmp/mypipe &

cat /tmp/mypipe > /path/to/final_output &

wait; wait; wait; wait

2
कब job1और job2उत्पादन लंबी (> 4096 बाइट्स) लाइनों से यह कैसे सुरक्षित रहेगा ? ऐसा लगता है कि नामकरण में बहुत पहले कोड के पाइप नाम के बराबर है।
वि।

बहुत उचित बिंदु। आपके प्रश्न में स्पष्ट रूप से बाहर बुलाए जाने के बावजूद मैंने बड़े-बूँद आउटपुट पर विचार नहीं किया। मैं अब सोच रहा था कि क्या शायद कोई ऐसा उपकरण नहीं है जो उल्टा करता है tee, जो जैसा चाहता है वैसा ही लगता है। संभवतः syslogया अन्य लॉगिंग टूल के आंतरिक को देखें , क्योंकि वे निश्चित रूप से कई स्थानों से एक लॉग फ़ाइल में आउटपुट करते हैं। लॉकिंग अच्छी तरह से सही उत्तर हो सकता है, जैसा कि @emmanual ने भी सुझाव दिया है।
डोपघोटी

0

पुराना प्रश्न, मुझे पता है, लेकिन मैं उसी के बारे में सोच रहा हूं, और यही मैं साथ आया हूं:

garbling_job | (
    while read LINE
    do
        echo $LINE
    done
) &

मुझे लगता है कि उनमें से कुछ को शुरू करने में सक्षम होने के लिए बिना गार्बल्ड आउटपुट के बारे में चिंता करने की क्षमता है।

संपादित करें: जैसा कि ओले ने सुझाव दिया है - आपको लंबी लाइनों के साथ सावधान रहना चाहिए (> 4k, विवरण के लिए ओल्स नीचे टिप्पणी देखें)

यहाँ मेरा परीक्षण कार्यक्रम है

if [ "$1" = "go" ]
then
for i in 1 2
do
    printf 111112222222222223333
    sleep .01
    printf 3333333444444444444555555555555
    sleep .01
    printf 6666666666666667777
    sleep .01
    printf 777777788888888889999999999999999
    sleep .01
    echo
done
exit
fi

# running them in sequence is all very fine
for i in 1 2 3 4 5 6 7 8
do
    echo bash $0 go 
done

# now this is all garbled up
for i in 1 2 3 4 5 6 7 8
do
    bash $0 go &
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# using cat inbetween does not make it better
for i in 1 2 3 4 5 6 7 8
do
    bash $0 go | cat &
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# it does not help to use stdbuff after the thing that just printfs sporadicall
for i in 1 2 3 4 5 6 7 8
do
    bash $0 go | stdbuf -oL cat &
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# it does not help to use stdbuff before either - or I am not understanding stdbuff
for i in 1 2 3 4 5 6 7 8
do
    stdbuf -o10000 bash $0 go | stdbuf -oL cat &
echo
done
for i in 1 2 3 4 5 6 7 8; do wait; done

# can I read - yes - they are now fine again
for i in 1 2 3 4 5 6 7 8
do
bash $0 go | (
    while read LINE
    do
        echo $LINE
    done
) &
echo
done
for i in 1 2 3 4 5 6 7 8; do wait; done

1
आपको उन पंक्तियों के साथ परीक्षण करना होगा जो आपके पृष्ठ आकार (आमतौर पर 4-8K) से बड़ी हैं। देखें mywiki.wooledge.org/...
ओले Tange

मैं थोड़ा असहमत। यदि आपके पास इस तरह के विविध आउटपुट हैं, तो आप एक पाइप में पूरी तरह से इको-मल्टीप्लेक्सिंग के अलावा कुछ करना चाहते हैं। पुराने जमाने के लॉग और स्टेटस मैसेज जैसी छोटी लाइनों के लिए, ऊपर काम करने के लिए लगता है और बहुत ही सरल है।
user2692263

यदि आप पहले से ही जानते हैं कि आपके समाधान के लिए एक सीमा है, तो उस पर विचार करें। UNIX लोगों के साथ मेरा अनुभव यह है कि यदि वे ऐसा समाधान देखते हैं जो n के लिए काम करता है तो वे मान लेते हैं कि यह n * 10 के लिए भी काम करेगा - जब तक कि यह नहीं समझाया जाता है कि n की कोई सीमा है। यदि आप समझा सकते हैं कि एन की सीमा क्यों है और इसे कैसे बढ़ाया जाए, तो यह और भी बेहतर है।
ओले तांगे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.