आप बैश में दो पाइपलाइनों को कैसे अलग कर सकते हैं?


143

आप कैसे कर सकते हैं diff बैश में अस्थायी फ़ाइलों का उपयोग किए बिना दो पाइपलाइनों? कहें कि आपके पास दो कमांड पाइपलाइन हैं:

foo | bar
baz | quux

और आप diffउनके आउटपुट में खोजना चाहते हैं । एक समाधान स्पष्ट रूप से होगा:

foo | bar > /tmp/a
baz | quux > /tmp/b
diff /tmp/a /tmp/b

क्या बैश में अस्थायी फ़ाइलों के उपयोग के बिना ऐसा करना संभव है? आप पाइप लाइनों में से किसी एक को अलग करने के लिए एक अस्थायी फ़ाइल से छुटकारा पा सकते हैं:

foo | bar > /tmp/a
baz | quux | diff /tmp/a -

लेकिन आप दोनों पाइपलाइनों को एक साथ अलग-अलग नहीं कर सकते (किसी भी स्पष्ट तरीके से नहीं, कम से कम)। /dev/fdअस्थायी फ़ाइलों का उपयोग किए बिना ऐसा करने के लिए कुछ चतुर चाल शामिल है?

जवाबों:


146

2 tmp फ़ाइलों के साथ एक-पंक्ति (जो आप चाहते हैं) वह होगी:

 foo | bar > file1.txt && baz | quux > file2.txt && diff file1.txt file2.txt

बैश के साथ , आप हालांकि कोशिश कर सकते हैं:

 diff <(foo | bar) <(baz | quux)

 foo | bar | diff - <(baz | quux)  # or only use process substitution once

2 संस्करण अधिक स्पष्ट रूप से आपको याद दिलाएगा कि कौन सा इनपुट था, जो दो गिने हुए fds के बजाय
-- /dev/stdinबनाम ++ /dev/fd/63या कुछ और दिखा रहा था ।


फाइलसिस्टम में एक नामांकित पाइप भी नहीं दिखाई देगा, कम से कम OSes पर जहां बैश फाइलनाम का उपयोग करके प्रक्रिया प्रतिस्थापन को लागू कर सकता है जैसे फ़ाइल नाम /dev/fd/63प्राप्त करने के लिए कमांड को खोल सकते हैं और पढ़ सकते हैं वास्तव में पहले से खुली हुई फ़ाइल विवरण से पढ़ सकते हैं जो bash सेट है कमांड निष्पादित करने से पहले। (यानी pipe(2)कांटे से पहले बैश का उपयोग किया जाता है , और फिर 63 वें पर इनपुट फ़ाइल डिस्क्रिप्टर dup2के आउटपुट से रीडायरेक्ट quuxकरने के लिए diff।)

सिस्टम पर कोई "जादुई" नहीं है , /dev/fdया /proc/self/fdbash प्रक्रिया प्रतिस्थापन को लागू करने के लिए नाम पाइप का उपयोग कर सकता है, लेकिन यह अस्थायी फ़ाइलों के विपरीत, कम से कम उन्हें स्वयं प्रबंधित करेगा, और आपका डेटा फ़ाइल सिस्टम को नहीं लिखा जाएगा।

आप यह जांच सकते हैं कि बैश इम्प्लीमेंटेशन कैसे echo <(true)इसे पढ़ने के बजाय फाइलनेम को प्रिंट करने के लिए प्रतिस्थापन की प्रक्रिया करता है। यह /dev/fd/63एक विशिष्ट लिनक्स सिस्टम पर प्रिंट करता है। या वास्तव में क्या सिस्टम कॉल बैश उपयोग करता है, इस बारे में अधिक जानकारी के लिए, लिनक्स सिस्टम पर यह कमांड फाइल और फाइल-डिस्क्रिप्टर सिस्टम कॉल का पता लगाएगा

strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'

बैश के बिना, आप एक नामित पाइप बना सकते हैं । STDIN से एक इनपुट पढ़ने के -लिए बताने के diffलिए उपयोग करें , और दूसरे के रूप में नामित पाइप का उपयोग करें:

mkfifo file1_pipe.txt
foo|bar > file1_pipe.txt && baz | quux | diff file1_pipe.txt - && rm file1_pipe.txt

ध्यान दें कि आप केवल टी आउटपुट के साथ कई इनपुट के लिए एक आउटपुट को पाइप कर सकते हैं :

ls *.txt | tee /dev/tty txtlist.txt 

उपरोक्त कमांड टर्मिनल के लिए ls * .txt का आउटपुट प्रदर्शित करता है और इसे टेक्स्ट फाइल txtlist.txt पर आउटपुट करता है।

लेकिन प्रक्रिया प्रतिस्थापन के साथ, आप teeएक ही डेटा को कई पाइपलाइनों में फीड करने के लिए उपयोग कर सकते हैं :

cat *.txt | tee >(foo | bar > result1.txt)  >(baz | quux > result2.txt) | foobar

5
बिना बैश के भी, आप mkfifo a; cmd >a& cmd2|diff a -; rm a
फीमो

आप एक आर्ग के लिए एक नियमित पाइप का उपयोग कर सकते हैं pipeline1 | diff -u - <(pipeline2):। तब आउटपुट आपको अधिक स्पष्ट रूप से याद दिलाएगा कि कौन सा इनपुट था, जो दो गिने हुए fds के बजाय, -- /dev/stdinबनाम ++ /dev/fd/67या कुछ दिखा कर।
पीटर कॉर्डेस

प्रक्रिया प्रतिस्थापन ( foo <( pipe )) फ़ाइल सिस्टम को संशोधित नहीं करता है। पाइप गुमनाम है ; इसका फाइल सिस्टम में कोई नाम नहीं है । शेल pipeसिस्टम कॉल का उपयोग करता है इसे बनाने के लिए, नहीं mkfifo। का प्रयोग करें strace -f -efile,desc,clone,execve bash -c '/bin/true | diff -u - <(/bin/true)'ट्रेस फ़ाइल और फ़ाइल वर्णनकर्ता सिस्टम कॉल करने के लिए करता है, तो आप खुद के लिए देखना चाहता हूँ। लिनक्स पर, वर्चुअल फाइल सिस्टम /dev/fd/63का हिस्सा है /proc; इसमें स्वचालित रूप से हर फ़ाइल डिस्क्रिप्टर के लिए प्रविष्टियाँ हैं, और यह सामग्री की प्रतिलिपि नहीं है। तो आप उस "अस्थायी फ़ाइल" को कॉल नहीं कर सकते जब तक कि foo 3<bar.txtमायने नहीं रखता
पीटर कॉर्डेस

@PeterCordes अच्छे अंक। अधिक दृश्यता के उत्तर में मैंने आपकी टिप्पणी शामिल की है।
VonC

1
@PeterCordes मैं आपको कोई भी संपादन छोड़ दूंगा: जो कि स्टैक ओवरफ्लो को दिलचस्प बनाता है: कोई भी किसी उत्तर को "ठीक" कर सकता है।
VonC

127

बैश में, आप उपधाराओं का उपयोग कर सकते हैं, कमांड पाइपलाइनों को व्यक्तिगत रूप से निष्पादित करने के लिए, कोष्ठक के भीतर पाइपलाइन को संलग्न करके। आप इसके बाद इनका उपसर्ग कर सकते हैं <अनाम नाम के पाइप बनाने के लिए जिन्हें आप बाद में अलग करने के लिए पास कर सकते हैं।

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

diff <(foo | bar) <(baz | quux)

अनाम नाम के पाइप को बाश द्वारा प्रबंधित किया जाता है, इसलिए वे स्वचालित रूप से बनाए जाते हैं और नष्ट हो जाते हैं (अस्थायी फ़ाइलों के विपरीत)।


1
अनाम समाधान - - एक ही समाधान पर मेरे रेडिएशन से बहुत अधिक विस्तृत। +1
वॉन


5

इस पृष्ठ पर पहुंचने वाले कुछ लोग लाइन-बाय-लाइन अंतर की तलाश कर सकते हैं, जिसके लिए commया grep -fइसके बजाय इसका उपयोग किया जाना चाहिए।

एक बात और बता दें कि, उत्तर के सभी उदाहरणों में, अंतर तब तक शुरू नहीं होगा जब तक कि दोनों धाराएँ समाप्त नहीं हो जातीं। इसे उदाहरण के साथ परखें:

comm -23 <(seq 100 | sort) <(seq 10 20 && sleep 5 && seq 20 30 | sort)

यदि यह एक समस्या है, तो आप sd (स्ट्रीम डिफरेंट) की कोशिश कर सकते हैं , जिसमें commऊपर दिए गए उदाहरणों की तरह न तो छंटनी की आवश्यकता होती है और न ही प्रक्रिया प्रतिस्थापन की, यह आदेश या परिमाण की तुलना में तेज़ है grep -f और अनंत धाराओं का समर्थन करता है।

मेरे द्वारा प्रस्तावित परीक्षा उदाहरण में इस तरह लिखा जाएगा sd:

seq 100 | sd 'seq 10 20 && sleep 5 && seq 20 30'

लेकिन अंतर यह है कि अभी के seq 100साथ अलग हो जाएगा seq 10। ध्यान दें, यदि धाराओं में से एक एक है tail -f, तो प्रक्रिया प्रतिस्थापन के साथ अंतर नहीं किया जा सकता है।

यहाँ एक ब्लॉगपोस्ट है जो मैंने टर्मिनल पर अलग-अलग धाराओं के बारे में लिखा है, जो परिचय देता है sd

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