मैं एक पाइप कैसे कर सकता हूं?


27

मैं timeएक कमांड चाहता हूं जिसमें एक पाइपिंग आउटपुट से दूसरे में दो अलग-अलग कमांड होते हैं। उदाहरण के लिए, नीचे दो लिपियों पर विचार करें:

$ cat foo.sh
#!/bin/sh
sleep 4

$ cat bar.sh
#!/bin/sh
sleep 2

अब, मुझे timeसमय लगने की सूचना कैसे मिल सकती है foo.sh | bar.sh(और हां, मुझे पता है कि पाइप का यहां कोई मतलब नहीं है, लेकिन यह सिर्फ एक उदाहरण है) यह अपेक्षा के अनुरूप काम करता है यदि मैं उन्हें बिना पाइपिंग के एक उप-क्रम में क्रमिक रूप से चलाता हूं:

$ time ( foo.sh; bar.sh )

real    0m6.020s
user    0m0.010s
sys     0m0.003s

लेकिन मुझे यह काम करने के लिए नहीं मिल सकता जब पाइपिंग:

$ time ( foo.sh | bar.sh )

real    0m4.009s
user    0m0.007s
sys     0m0.003s

$ time ( { foo.sh | bar.sh; } )

real    0m4.008s
user    0m0.007s
sys     0m0.000s

$ time sh -c "foo.sh | bar.sh "

real    0m4.006s
user    0m0.000s
sys     0m0.000s

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

$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00

यह काम नहीं करता है अगर मैं एक तीसरी स्क्रिप्ट बनाता हूं जो केवल पाइप चलाता है:

$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh

और फिर उस समय:

$ time baz.sh

real    0m4.009s
user    0m0.003s
sys     0m0.000s

दिलचस्प बात यह है timeकि यह पहली कमांड होते ही बाहर नहीं निकलता है। अगर मैं इसे बदलूं bar.sh:

#!/bin/sh
sleep 2
seq 1 5

और फिर timeदोबारा, मैं उम्मीद कर रहा था कि timeआउटपुट प्रिंट होने से पहले होगा seqलेकिन यह नहीं है:

$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5

real    0m4.005s
user    0m0.003s
sys     0m0.000s

ऐसा लगता है कि इसकी रिपोर्ट 1 को प्रिंट करने से पहले खत्म होने के इंतजार के बावजूद इसे timeनिष्पादित करने में लगने वाले समय की गिनती नहीं है ।bar.sh

सभी परीक्षण एक आर्क सिस्टम पर चलाए गए और बैश 4.4.12 (1) -release का उपयोग किया गया। मैं केवल प्रोजेक्ट के लिए बैश का उपयोग कर सकता हूं, यह एक हिस्सा है भले ही zshया कोई अन्य शक्तिशाली शेल इसके चारों ओर मिल सकता है, जो मेरे लिए एक व्यवहार्य समाधान नहीं होगा।

तो, मुझे समय कैसे मिल सकता है जब पाइप किए गए कमांड का एक सेट चलाने के लिए लिया गया हो? और, जब हम इस पर हैं, तो यह काम क्यों नहीं करता है? ऐसा लगता है कि जैसे timeही पहला कमांड समाप्त हुआ , तुरंत बाहर निकल गया। क्यूं कर?

मुझे पता है कि मैं व्यक्तिगत समय को कुछ इस तरह से प्राप्त कर सकता हूं:

( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time

लेकिन मैं अभी भी जानना चाहूंगा कि क्या पूरे ऑपरेशन को एकल ऑपरेशन के रूप में करना संभव है।


1 यह एक बफर मुद्दा हो प्रतीत नहीं होता है, मैं के साथ स्क्रिप्ट चला की कोशिश की unbufferedऔर stdbuf -i0 -o0 -e0और यह संख्या अब भी पहले मुद्रित हुए timeउत्पादन।


क्या आपने इसे भौतिक स्टॉपवॉच के साथ आज़माया है?
पेरीसिनेशन

@pericynthion yep अंततः मैंने किया। और यह भी दिखाया कि उत्तर क्या समझाते हैं: समय वास्तव में काम कर रहा है लेकिन (स्पष्ट रूप से पर्याप्त और जैसा कि मुझे एहसास होना चाहिए) पाइपलाइन में आदेश समवर्ती रूप से चलते हैं इसलिए लिया गया समय अनिवार्य रूप से सबसे धीमा समय है।
terdon

जवाबों:


33

यह है काम कर रहे।

एक पाइपलाइन के विभिन्न हिस्सों को समवर्ती रूप से निष्पादित किया जाता है। केवल एक चीज जो पाइपलाइन में प्रक्रियाओं को सिंक्रनाइज़ / अनुक्रमित करती है, वह है IO, यानी पाइपलाइन में अगली प्रक्रिया को लिखने वाली एक प्रक्रिया और अगली प्रक्रिया जो पहले लिखती है उसे पढ़ती है। इसके अलावा, वे स्वतंत्र रूप से एक दूसरे को निष्पादित कर रहे हैं ।

चूंकि आपकी पाइपलाइन में प्रक्रियाओं के बीच कोई रीडिंग या लेखन नहीं हो रहा है, पाइपलाइन को निष्पादित करने में लगने वाला समय सबसे लंबी sleepकॉल है।

आपने भी लिखा होगा

time ( foo.sh & bar.sh &; wait )

Terdon ने चैट में थोड़ा संशोधित उदाहरण स्क्रिप्ट के एक जोड़े को पोस्ट किया :

#!/bin/sh
# This is "foo.sh"
echo 1; sleep 1
echo 2; sleep 1
echo 3; sleep 1
echo 4

तथा

#!/bin/sh
# This is "bar.sh"
sleep 2
while read line; do
  echo "LL $line"
done
sleep 1

क्वेरी थी " time ( sh foo.sh | sh bar.sh )3 + 3 = 6 सेकंड के बजाय 4 सेकंड क्यों लौटाता है?"

यह देखने के लिए कि प्रत्येक कमांड निष्पादित होने के समय सहित क्या हो रहा है, कोई भी ऐसा कर सकता है (आउटपुट में मेरे एनोटेशन शामिल हैं):

$ time ( env PS4='$SECONDS foo: ' sh -x foo.sh | PS4='$SECONDS bar: ' sh -x bar.sh )
0 bar: sleep 2
0 foo: echo 1     ; The output is buffered
0 foo: sleep 1
1 foo: echo 2     ; The output is buffered
1 foo: sleep 1
2 bar: read line  ; "bar" wakes up and reads the two first echoes
2 bar: echo LL 1
LL 1
2 bar: read line
2 bar: echo LL 2
LL 2
2 bar: read line  ; "bar" waits for more
2 foo: echo 3     ; "foo" wakes up from its second sleep
2 bar: echo LL 3
LL 3
2 bar: read line
2 foo: sleep 1
3 foo: echo 4     ; "foo" does the last echo and exits
3 bar: echo LL 4
LL 4
3 bar: read line  ; "bar" fails to read more
3 bar: sleep 1    ; ... and goes to sleep for one second

real    0m4.14s
user    0m0.00s
sys     0m0.10s

तो, समाप्त करने के लिए, पाइपलाइन पहले दो कॉल के उत्पादन की बफरिंग के कारण, 4 सेकंड, नहीं 6 लेता echoमें foo.sh


1
@terdon मान sums हैं, लेकिन स्क्रिप्ट बहुत कम उपयोगकर्ता और सिस्टम समय लेती हैं - वे बस इंतजार करते हैं, जो गिनती नहीं करता है (दीवार-घड़ी के समय को छोड़कर)।
स्टीफन किट

2
ध्यान दें कि बॉर्न शेल की तरह कुछ गोले या ksh93केवल पाइपलाइन के अंतिम घटक की प्रतीक्षा करें ( sleep 3 | sleep 11 सेकंड तक चलेगा)। बॉर्न शेल का कोई timeकीवर्ड नहीं है , लेकिन ksh93जब इसके साथ चलाया जाता है time, तो सभी घटकों का इंतजार किया जाता है।
स्टीफन चेज़लस

3
मैं सिर्फ कह रहा है कि एक ही है कि खोजने के लिए आश्चर्यचकित हो सकता है sleep 10 | sleep 1एक सेकंड समय लगता है time sleep 10 | sleep 1ksh93 में 10 सेकंड लेता है। बॉर्न शेल में time sleep 10 | sleep 1एक सेकंड लगेगा, लेकिन आपको 9 सेकंड बाद ब्लू आउटपुट आउट टाइम ( sleep 10केवल और के लिए /usr/bin/time) मिलेगा ।
स्टीफन चेज़लस

1
यह किसी भी चीज़ की रखवाली नहीं है। timeपाइपलाइन का सही समय पर, लेकिन ksh93 में शेल के व्यवहार को बदल देता है। (sleep 10 | sleep 1)1 सेकंड time (sleep 10 | sleep 1)लेता है , 10 सेकंड लेता है। { (sleep 10 | sleep 1); echo x; }आउटपुट x1 सेकंड के बाद, time { (sleep 10 | sleep 1); echo x; }आउटपुट x10 सेकंड के बाद। यदि आप उस कोड को किसी फंक्शन और फंक्शन में लगाते हैं।
स्टीफन चेजालस

1
ध्यान दें कि में ksh93में की तरह zsh( -o promptsubstयहाँ), आप कर सकते हैं typeset -F SECONDSएक कम पाने के लिए अनुमानित सेकंड की संख्या (इसे POSIX shनहीं है SECONDS)
स्टीफन Chazelas

10

क्या यह एक बेहतर उदाहरण होगा?

$ time perl -e 'alarm(3); 1 while 1;' | perl -e 'alarm(4); 1 while 1;'
Alarm clock

real    0m4.004s
user    0m6.992s
sys     0m0.004s

स्क्रिप्ट्स 3 और 4 सेकंड के लिए व्यस्त हैं (सम्मान।), समानांतर निष्पादन के कारण वास्तविक समय में कुल 4 सेकंड और CPU समय के 7 सेकंड। (कम से कम लगभग।)

या यह:

$ time ( sleep 2; echo) | ( read x; sleep 3 )

real    0m5.004s
user    0m0.000s
sys     0m0.000s

ये समानांतर में नहीं चलते हैं, इसलिए लिया गया कुल समय 5 सेकंड है। यह सब सोने में खर्च होता है, इसलिए कोई सीपीयू समय का उपयोग नहीं करता है।


3

यदि आपके पास sysdigआप मनमाने ढंग से बिंदुओं पर ट्रेसर सम्मिलित कर सकते हैं , तो यह मानते हुए कि आप आवश्यक राइट्स को जोड़ने के लिए कोड को संशोधित कर सकते हैं/dev/null

echo '>::blah::' >/dev/null
foo.sh | bar.sh
echo '<::blah::' >/dev/null

(लेकिन यह आपकी "एकल ऑपरेशन" आवश्यकता को विफल करता है) और फिर चीजों को रिकॉर्ड करके

$ sudo sysdig -w blalog "span.tags contains blah"

और फिर आपको शायद सिर्फ ड्यूरेशन एक्सपोर्ट करने के लिए एक sddig छेनी की आवश्यकता होगी

description = "Exports sysdig span tag durations";
short_description = "Export span tag durations.";
category = "Tracers";

args = {}

function on_init()
    ftags = chisel.request_field("span.tags")
    flatency = chisel.request_field("span.duration")
    chisel.set_filter("evt.type=tracer and evt.dir=<")
    return true
end

function on_event()
    local tags = evt.field(ftags)
    local latency = evt.field(flatency)
    if latency then
        print(tostring(tags) .. "\t" .. tonumber(latency) / 1e9)
    end
    return true
end

जो एक बार sysdig/chiselsफ़ाइल के रूप में spantagduration.luaइस्तेमाल किया जा सकता है के रूप में अपनी निर्देशिका के लिए बचाया

$ sysdig -r blalog -c spantagduration
...

या आप csysdigJSON आउटपुट के साथ या उसके आसपास खेल सकते हैं ।

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