सवाल "पुनर्निर्देशन" और "पाइप" के बीच क्या अंतर है?


यह सवाल थोड़ा बेवकूफ लग सकता है, लेकिन मैं वास्तव में पुनर्निर्देशन और पाइप के बीच अंतर नहीं देख सकता।

रीडायरेक्शन का उपयोग stdout / stdin / stderr को पुनर्निर्देशित करने के लिए किया जाता है, उदा। ls > log.txt

पाइप का उपयोग किसी अन्य कमांड में इनपुट के रूप में कमांड के आउटपुट देने के लिए किया जाता है, उदा। ls | grep file.txt

लेकिन एक ही चीज़ के लिए दो ऑपरेटर क्यों हैं?

क्यों न लिखें ls > grep आउटपुट पास करने के लिए, क्या यह सिर्फ एक तरह का पुनर्निर्देशन नहीं है? मुझे क्या याद आ रहा है


174
2017-08-07 13:22


मूल




जवाब:


पाइप का उपयोग दूसरे को आउटपुट पास करने के लिए किया जाता है कार्यक्रम या उपयोगिता

रीडायरेक्ट का उपयोग या तो आउटपुट पास करने के लिए किया जाता है फ़ाइल या स्ट्रीम

उदाहरण: thing1 > thing2 बनाम thing1 | thing2

thing1 > thing2

  1. आपका खोल नाम का कार्यक्रम चलाएगा thing1
  2. सब कुछ जो thing1 आउटपुट को एक फाइल में रखा जाएगा thing2। (नोट - अगर thing2 मौजूद है, इसे ओवरराइट किया जाएगा)

यदि आप प्रोग्राम से आउटपुट पास करना चाहते हैं thing1 एक कार्यक्रम के लिए बुलाया thing2, आप निम्न कार्य कर सकते हैं:

thing1 > temp_file && thing2 < temp_file

जो कि

  1. रन कार्यक्रम चलाया thing1
  2. आउटपुट को नाम की एक फ़ाइल में सहेजें temp_file
  3. रन कार्यक्रम चलाया thing2, यह दिखाते हुए कि कीबोर्ड पर व्यक्ति ने सामग्री टाइप की है temp_file इनपुट के रूप में।

हालांकि, यह घबराहट है, इसलिए उन्होंने पाइप को ऐसा करने के लिए एक आसान तरीका बना दिया। thing1 | thing2 वही काम करता है thing1 > temp_file && thing2 < temp_file

टिप्पणी में प्रश्न के लिए अधिक जानकारी प्रदान करने के लिए संपादित करें:

अगर > "कार्यक्रम में पास" और "फ़ाइल में लिखने" दोनों होने की कोशिश की, यह दोनों दिशाओं में समस्याएं पैदा कर सकता है।

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

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

तीसरा: यदि आप कमांड टाइप करते हैं, तो यह आपको चेतावनी नहीं देगा कि आदेश मौजूद नहीं है। अभी, अगर आप टाइप करते हैं ls | gerp log.txt यह आपको बताएगा bash: gerp: command not found। अगर > दोनों का मतलब है, यह केवल आपके लिए एक नई फाइल तैयार करेगा (फिर चेतावनी दें कि यह नहीं जानता कि इसके साथ क्या करना है log.txt)।


195
2017-08-07 13:30



धन्यवाद। आपने उल्लिखित किया था thing1 > temp_file && thing2 < temp_fileपाइप के साथ और अधिक आसान करने के लिए। लेकिन फिर से उपयोग क्यों नहीं करें > ऑपरेटर ऐसा करने के लिए, उदा। thing1 > thing2 आदेशों के लिए thing1 तथा thing2 ? एक अतिरिक्त ऑपरेटर क्यों | ? - John Threepwood
"आउटपुट लें और इसे एक फाइल में लिखें" आउटपुट लें और इसे एक अलग प्रोग्राम में पास करें "की तुलना में एक अलग कार्रवाई है। मैं अपने उत्तरों में और विचारों को संपादित करूंगा ... - David Oneill
@ जॉन थ्रीपवुड उनके अलग-अलग अर्थ हैं। अगर मैं किसी फ़ाइल नाम पर कुछ रीडायरेक्ट करना चाहता हूं तो क्या होगा less, उदाहरण के लिए? thing | less तथा thing > less पूरी तरह से अलग हैं, क्योंकि वे अलग-अलग चीजें करते हैं। आप जो प्रस्ताव देते हैं वह अस्पष्टता पैदा करेगा। - Darkhogg
क्या यह कहना सही है कि "thing1> temp_file" केवल "thing1 | tee temp_file" के लिए वाक्य रचनात्मक चीनी है? टीई के बारे में पता लगाने के बाद से मैं लगभग कभी भी रीडायरेक्ट का उपयोग नहीं करता हूं। - Sridhar-Sarnobat
@ श्रीधर-सरनोबत संख्या, द tee आदेश कुछ अलग करता है। tee दोनों स्क्रीन पर आउटपुट लिखता है (stdout) तथा फ़ाइल। रीडायरेक्ट करता है केवल फ़ाइल। - David Oneill


अगर इसका अर्थ है foo > bar इस पर निर्भर करेगा कि नाम कमांड है या नहीं bar जो पुनर्निर्देशन का उपयोग बहुत कठिन और अधिक त्रुटि प्रवण का उपयोग करेगा: हर बार जब मैं किसी फ़ाइल पर रीडायरेक्ट करना चाहता हूं तो मुझे पहले यह जांचना पड़ता था कि मेरे गंतव्य फ़ाइल की तरह नाम दिया गया है या नहीं।


19
2017-08-07 13:40



यह केवल एक मुद्दा होगा यदि आप लिख रहे हैं bar एक निर्देशिका में जो आपके हिस्से का हिस्सा है $PATH एनवी चर। यदि आप कुछ / बिन की तरह हैं, तो ओटी एक समस्या हो सकती है। किंतु इसके बावजूद, bar निष्पादन योग्य अनुमति सेट होना होगा, ताकि शैल केवल निष्पादन योग्य खोजने के लिए जांच न करे bar लेकिन वास्तव में इसे निष्पादित कर सकते हैं। और यदि चिंता मौजूदा फाइल को ओवरराइट करने के साथ है, noclober खोल विकल्प को मौजूदा फाइलों को पुनर्निर्देशन में ओवरराइट करना बंद करना चाहिए। - Sergiy Kolodyazhnyy


दो ऑपरेटरों के बीच एक महत्वपूर्ण अंतर है:

  1. ls > log.txt -> यह आदेश आउटपुट को log.txt फ़ाइल में भेजता है।

  2. ls | grep file.txt -> यह आदेश पाइप के उपयोग के माध्यम से ls के grep कमांड के आउटपुट भेजता है (|), और grep कमांड पिछले कमांड द्वारा दिए गए इनपुट में file.txt की खोज करता है।

यदि आपको पहले परिदृश्य का उपयोग करके एक ही कार्य करना था, तो यह होगा:

ls > log.txt; grep 'file.txt' log.txt

तो एक पाइप (के साथ |) आउटपुट को अन्य कमांड को भेजने के लिए प्रयोग किया जाता है, जबकि पुनर्निर्देशन (के साथ >) आउटपुट को कुछ फाइल में रीडायरेक्ट करने के लिए प्रयोग किया जाता है।


11
2017-08-07 13:32





यूनिक्स और लिनक्स सिस्टम प्रशासन पुस्तिका से:

पुनर्निर्देशन

शेल दोहराने के निर्देशों के रूप में प्रतीकों <,>, और >> को व्याख्या करता है आदेश के इनपुट या आउटपुट से या से फ़ाइल

पाइप्स

एक के STDOUT को जोड़ने के लिए आदेश एसटीडीआईएन के लिए एक और का प्रयोग करें | प्रतीक, आमतौर पर एक पाइप के रूप में जाना जाता है।

तो मेरी व्याख्या है: यदि यह आदेश देने का आदेश है, तो पाइप का उपयोग करें। यदि आप फ़ाइल में या से आउटपुट कर रहे हैं तो रीडायरेक्ट का उपयोग करें।


9
2018-02-16 00:40





दोनों के बीच एक बड़ा वाक्य रचना अंतर है:

  1. एक रीडायरेक्ट एक प्रोग्राम के लिए एक तर्क है
  2. एक पाइप दो आदेश अलग करता है

आप इस तरह के रीडायरेक्ट के बारे में सोच सकते हैं: cat [<infile] [>outfile]। इसका मतलब है कि आदेश कोई फर्क नहीं पड़ता: cat <infile >outfile के समान है cat >outfile <infile। आप अन्य तर्कों के साथ रीडायरेक्ट भी मिश्रण कर सकते हैं: cat >outfile <infile -b तथा cat <infile -b >outfile दोनों बिल्कुल ठीक हैं। इसके अलावा आप एक से अधिक इनपुट या आउटपुट को एकसाथ स्ट्रिंग कर सकते हैं (इनपुट अनुक्रमिक रूप से पढ़ा जाएगा और सभी आउटपुट प्रत्येक आउटपुट फ़ाइल में लिखे जाएंगे): cat >outfile1 >outfile2 <infile1 <infile2। रीडायरेक्ट का लक्ष्य या स्रोत या तो एक फ़ाइल नाम या स्ट्रीम का नाम हो सकता है (जैसे & 1, कम से कम बैश में)।

लेकिन पाइप पूरी तरह से एक कमांड से एक कमांड को अलग करते हैं, आप उन्हें तर्क के साथ मिश्रण नहीं कर सकते हैं:

[command1] | [command2]

पाइप कमांड 1 से मानक आउटपुट में लिखे गए सब कुछ लेता है और इसे कमांड 2 के मानक इनपुट में भेजता है।

आप पाइपिंग और पुनर्निर्देशन को भी जोड़ सकते हैं। उदाहरण के लिए:

cat <infile >outfile | cat <infile2 >outfile2

सबसे पहला cat infile से लाइनें पढ़ेगा, फिर साथ ही प्रत्येक पंक्ति को बाहर निकालने के लिए लिखें और इसे दूसरे को भेजें cat

क्षण में cat, मानक इनपुट पहले पाइप (इन्फाइल की सामग्री) से पढ़ता है, फिर infile2 से पढ़ता है, प्रत्येक पंक्ति को outfile2 पर लिखता है। इसे चलाने के बाद, आउटफाइल इन्फाइल की एक प्रति होगी, और आउटफाइल 2 में infile2 के बाद infile शामिल होगा।

अंत में, आप वास्तव में "यहां स्ट्रिंग" पुनर्निर्देशन (केवल बैश परिवार) और बैकटीक्स का उपयोग करके अपने उदाहरण के समान कुछ करते हैं:

grep blah <<<`ls`

जैसा ही परिणाम देगा

ls | grep blah

लेकिन मुझे लगता है कि पुनर्निर्देशन संस्करण पहले एलएस के सभी आउटपुट को बफर (मेमोरी में) में पढ़ेगा, और फिर उस बफर को एक समय में एक लाइन को grep करने के लिए फ़ीड करेगा, जबकि पाइप संस्करण प्रत्येक पंक्ति को एलएस से ले जाएगा क्योंकि यह उभरता है, और उस लाइन को grep को पास करें।


3
2017-08-23 22:24



नाइटपिक: अगर आप एक एफडी को दूसरे पर रीडायरेक्ट करते हैं तो रीडायरेक्शन में ऑर्डर मायने रखता है: echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah इसके अलावा, फ़ाइल में पुनर्निर्देशन केवल अंतिम पुनर्निर्देशन का उपयोग करेगा। echo yes >/tmp/blah >/tmp/blah2 केवल लिखेंगे /tmp/blah2। - muru
रीडायरेक्ट वास्तव में कार्यक्रम के लिए तर्क नहीं है। कार्यक्रम यह नहीं जानता कि उसका आउटपुट कहां जाता है (या इनपुट आता है)। कार्यक्रम चलाने से पहले चीजों को व्यवस्थित करने के तरीके को बताने का यह एकमात्र तरीका है। - Alois Mahdal


नोट: उत्तर इन तंत्रों की अपनी समझ को प्रतिबिंबित करता है, इस साइट पर सहकर्मियों द्वारा किए गए उत्तरों के शोध और पढ़ने पर जमा हुआ है और unix.stackexchange.com, और समय के साथ अद्यतन किया जाएगा के रूप में अद्यतन किया जाएगा। प्रश्न पूछने में संकोच न करें या टिप्पणियों में सुधार का सुझाव न दें। मैं यह भी सुझाव देता हूं कि आप यह देखने का प्रयास करें कि शेल में सिस्को कैसे काम करते हैं strace आदेश। कृपया इंटर्न या सिस्कोल की धारणा से भयभीत न हों - आपको यह समझने के लिए कि उन्हें शैल कैसे काम करता है, उन्हें समझने या उपयोग करने में सक्षम नहीं है, लेकिन वे निश्चित रूप से समझने में मदद करते हैं।

टी एल; डॉ

  • | पाइप डिस्क पर एक प्रविष्टि से जुड़े नहीं हैं, इसलिए एक इनोड नहीं है डिस्क फाइल सिस्टम की संख्या (लेकिन इनोड में है pipefs कर्नेल-स्पेस में आभासी फाइल सिस्टम), लेकिन रीडायरेक्शन में अक्सर फाइलें शामिल होती हैं, जिनमें डिस्क प्रविष्टियां होती हैं और इसलिए संबंधित इनोड होता है।
  • पाइप नहीं हैं lseek()'सक्षम ऐसे आदेश कुछ डेटा नहीं पढ़ सकते हैं और फिर वापस रिवाइंड कर सकते हैं, लेकिन जब आप रीडायरेक्ट करते हैं > या < आमतौर पर यह एक फाइल है जो है lseek() सक्षम ऑब्जेक्ट, इसलिए आदेश कृपया नेविगेट कर सकते हैं।
  • पुनर्निर्देशन फाइल डिस्क्रिप्टर पर हेरफेर हैं, जो कई हो सकते हैं; पाइप में केवल दो फ़ाइल वर्णक हैं - एक बाएं कमांड के लिए और एक दाएं कमांड के लिए
  • मानक धाराओं और पाइपों पर पुनर्निर्देशन दोनों buffered हैं।
  • पाइप लगभग हमेशा फोर्किंग, पुनर्निर्देशन शामिल हैं - हमेशा नहीं
  • पाइप हमेशा फाइल डिस्क्रिप्टर, रीडायरेक्शन से निपटते हैं - या तो डिस्क पर फ़ाइल नाम के साथ वास्तविक फाइलों का उपयोग करें, या फाइल डिस्क्रिप्टर।
  • पाइप इंटर-प्रोसेस कम्युनिकेशन विधि हैं, जबकि रीडायरेक्शन केवल खुली फाइलों या फ़ाइल जैसी वस्तुओं पर हेरफेर हैं
  • दोनों नियोजित करें dup2() फाइल डिस्क्रिप्टर की प्रतियां प्रदान करने के लिए हुड के नीचे सिस्को, जहां डेटा का वास्तविक प्रवाह होता है।
  • पुनर्निर्देशन "वैश्विक रूप से" के साथ लागू किया जा सकता है exec अंतर्निहित कमांड (देखें इस तथा इस ), तो अगर आप करते हैं exec > output.txt हर आदेश लिखेंगे output.txt उसके बाद से। | पाइप केवल वर्तमान कमांड के लिए लागू होते हैं (जिसका अर्थ है या तो सरल कमांड या सबहेल seq 5 | (head -n1; head -n2) या यौगिक आदेश।
  • जब फाइलों पर पुनर्निर्देशन किया जाता है, तो चीजें echo "TEST" > file तथा echo "TEST" >> file दोनों का उपयोग करें open() उस फ़ाइल पर syscall (यह भी देखें) और इसे पास करने के लिए फ़ाइल डिस्क्रिप्टर प्राप्त करें dup2()। पाइप्स | केवल उपयोग करें pipe() तथा dup2() syscall।

परिचय

यह समझने के लिए कि इन दो तंत्रों में भिन्नता कैसे है, उनके आवश्यक गुणों, दोनों के पीछे इतिहास, और सी की प्रोग्रामिंग भाषा में उनकी जड़ों को समझना आवश्यक है। वास्तव में, यह जानने के लिए कि कौन सी फाइल डिस्क्रिप्टर हैं, और कैसे dup2() तथा pipe() सिस्टम कॉल काम आवश्यक है, साथ ही साथ lseek()। शैल उपयोगकर्ता को इन तंत्रों को अमूर्त बनाने का एक तरीका है, लेकिन अमूर्तता से गहराई से खुदाई करने से शैल के व्यवहार की वास्तविक प्रकृति को समझने में मदद मिलती है।

रीडायरेक्शन और पाइप्स की उत्पत्ति

डेनिस रिच के लेख के अनुसार पैगंबर पेट्रोग्लिफ्स, पाइप एक से उत्पन्न हुआ 1 9 64 आंतरिक ज्ञापन द्वारा मैल्कम डगलस मैकइलॉयय, उस समय जब वे काम कर रहे थे मल्टीक्स ऑपरेटिंग सिस्टम। उद्धरण:

मेरी सबसे मजबूत चिंताओं को संक्षेप में डालने के लिए:

  1. हमारे पास बगीचे की नली जैसे कार्यक्रमों को जोड़ने के कुछ तरीके हो सकते हैं - किसी दूसरे सेगमेंट में स्क्रू करें जब यह किसी अन्य तरीके से डेटा मालिश करने के लिए आवश्यक हो जाता है। यह आईओ का भी तरीका है।

क्या स्पष्ट है कि उस समय प्रोग्राम डिस्क पर लिखने में सक्षम थे, हालांकि आउटपुट बड़ा होने पर यह अक्षम था। ब्रायन कर्निघन के स्पष्टीकरण को उद्धृत करने के लिए यूनिक्स पाइपलाइन वीडियो :

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

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

गहरी जा रही है: खोल के सिस्को और आंतरिक कार्य

हम धारणा से शुरू करते हैं फाइल डिस्क्रिप्टर। फ़ाइल वर्णनकर्ता मूल रूप से एक खुली फ़ाइल का वर्णन करते हैं (चाहे वह डिस्क पर, या स्मृति में, या अज्ञात फ़ाइल में फ़ाइल हो), जिसे पूर्णांक संख्या द्वारा दर्शाया गया है। दो मानक डेटा स्ट्रीम  (stdin, stdout, stderr) फ़ाइल क्रमशः 0,1, और 2 फ़ाइल हैं। वे कहां से आते हैं ? खैर, शेल कमांड में फ़ाइल डिस्क्रिप्टर को उनके माता-पिता से प्राप्त किया जाता है - खोल। और यह सामान्य रूप से सभी प्रक्रियाओं के लिए सच है - बाल प्रक्रिया माता-पिता के फ़ाइल वर्णनकर्ताओं को विरासत में लेती है। के लिये डेमॉन सभी विरासत फ़ाइल डिस्क्रिप्टर को बंद करना और / या अन्य स्थानों पर रीडायरेक्ट करना आम है।

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

[n]>word

उस [n] फाइल डिस्क्रिप्टर नंबर है। जब तुम करोगे echo "Something" > /dev/null संख्या 1 वहां निहित है, और echo 2> /dev/null

हुड के नीचे यह फ़ाइल डिस्क्रिप्टर के माध्यम से डुप्लिकेट करके किया जाता है dup2() सिस्टम कॉल चलो ले लो df > /dev/null। खोल एक बाल प्रक्रिया कहाँ बनायेगा df चलता है, लेकिन इससे पहले यह खुल जाएगा /dev/null फाइल डिस्क्रिप्टर # 3 के रूप में, और dup2(3,1) जारी किया जाएगा, जो फाइल डिस्क्रिप्टर 3 की एक प्रति बनाता है और प्रतिलिपि 1 होगी। आप जानते हैं कि आपके पास दो फाइलें हैं file1.txt तथा file2.txt, और जब आप करते हैं cp file1.txt file2.txt आपके पास दो समान फाइलें होंगी, लेकिन आप उन्हें स्वतंत्र रूप से कुशल बना सकते हैं? यह वही बात है जो यहां हो रही है। अक्सर आप देख सकते हैं कि दौड़ने से पहले, bash करूँगा dup(1,10) प्रतिलिपि फ़ाइल डिस्क्रिप्टर # 1 बनाने के लिए stdout (और वह प्रतिलिपि fd # 10 होगी) इसे बाद में पुनर्स्थापित करने के लिए। महत्वपूर्ण यह ध्यान रखना है कि जब आप विचार करते हैं अंतर्निहित आदेश (जो कि खोल का हिस्सा हैं, और इसमें कोई फ़ाइल नहीं है /bin या कहीं और) या गैर-इंटरैक्टिव खोल में सरल आदेश, खोल एक बाल प्रक्रिया नहीं बनाता है।

और फिर हमारे पास चीजें हैं [n]>&[m] तथा [n]&<[m]। यह फाइल डिस्क्रिप्टर डुप्लिकेट कर रहा है, जो एक ही तंत्र के रूप में है dup2() केवल अब यह शेल सिंटैक्स में है, जो आसानी से उपयोगकर्ता के लिए उपलब्ध है।

पुनर्निर्देशन के बारे में ध्यान देने योग्य महत्वपूर्ण बातों में से एक यह है कि उनका आदेश तय नहीं किया गया है, लेकिन यह महत्वपूर्ण है कि शैल किस प्रकार उपयोगकर्ता चाहता है कि व्याख्या करता है। निम्नलिखित की तुलना करें:

# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/  3>&2  2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd

# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/    2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd

शैल स्क्रिप्टिंग में इन का व्यावहारिक उपयोग बहुमुखी हो सकता है:

और बहुत दूसरे।

नलसाजी के साथ pipe() तथा dup2()

तो पाइप कैसे बनते हैं? के जरिए pipe() syscall, जो एक सरणी (उर्फ सूची) इनपुट के रूप में ले जाएगा pipefd प्रकार के दो आइटमों में से int (पूर्णांक)। वे दो पूर्णांक फ़ाइल वर्णनकर्ता हैं। pipefd[0] पाइप के पढ़ने के अंत होगा और pipefd[1] लेखन अंत होगा। तो अंदर df | grep 'foo', grep की प्रति प्राप्त होगी pipefd[0] तथा df की एक प्रति प्राप्त होगी pipefd[1]। पर कैसे ? बेशक, जादू के साथ dup2() syscall। के लिये df हमारे उदाहरण में, मान लें pipefd[1] # 4 है, तो खोल एक बच्चा बना देगा, करो dup2(4,1) (याद रखो मेरा cp उदाहरण?), और फिर करें execve() वास्तव में चलाने के लिए df। सहज रूप में, df फ़ाइल डिस्क्रिप्टर # 1 का उत्तराधिकारी होगा, लेकिन यह अनजान होगा कि यह टर्मिनल पर इंगित नहीं कर रहा है, लेकिन वास्तव में fd # 4, जो वास्तव में पाइप का लेखन अंत है। स्वाभाविक रूप से, एक ही चीज़ के साथ होगा grep 'foo' फाइल डिस्क्रिप्टर की विभिन्न संख्याओं को छोड़कर।

अब, दिलचस्प सवाल: क्या हम पाइप बना सकते हैं जो fd # 2 को भी रीडायरेक्ट करते हैं, न केवल fd # 1? हाँ, वास्तव में यही है |& बाश में करता है POSIX मानक को समर्थन के लिए खोल कमांड भाषा की आवश्यकता होती है df 2>&1 | grep 'foo' उस उद्देश्य के लिए वाक्यविन्यास, लेकिन bash कर देता है |& भी।

ध्यान रखना महत्वपूर्ण है कि पाइप हमेशा फाइल डिस्क्रिप्टर से निपटते हैं। वहां मौजूद FIFO या नामित पाइप, जिसमें डिस्क पर एक फ़ाइल नाम है और चलिए आप इसे फ़ाइल के रूप में उपयोग करते हैं, लेकिन एक पाइप की तरह व्यवहार करते हैं। लेकिन वो | पाइप के प्रकार जिन्हें अज्ञात पाइप के रूप में जाना जाता है - उनके पास कोई फ़ाइल नाम नहीं है, क्योंकि वे वास्तव में केवल दो ऑब्जेक्ट्स एक साथ जुड़े हुए हैं। तथ्य यह है कि हम फाइलों से निपट नहीं रहे हैं, यह भी एक महत्वपूर्ण निहितार्थ बनाता है: पाइप नहीं हैं lseek()'योग्य। फ़ाइलों, या तो मेमोरी या डिस्क में, स्थिर हैं - प्रोग्राम उपयोग कर सकते हैं lseek() बाइट 120 पर कूदने के लिए syscall, फिर बाइट 10 पर वापस, फिर अंत तक सभी तरह आगे बढ़ें। पाइप्स स्थैतिक नहीं हैं - वे अनुक्रमिक हैं, और इसलिए आप उन डेटा को रिवाइंड नहीं कर सकते हैं जिनसे आप उन्हें प्राप्त करते हैं lseek()। यह कुछ प्रोग्रामों को जागरूक करता है अगर वे फ़ाइल से या पाइप से पढ़ रहे हैं, और इस प्रकार वे कुशल प्रदर्शन के लिए आवश्यक समायोजन कर सकते हैं; दूसरे शब्दों में, ए prog अगर मैं कर सकता हूं तो पता लगा सकता हूं cat file.txt | prog या prog < input.txt। इसका असली काम उदाहरण है पूंछ

पाइप की अन्य दो बहुत ही रोचक संपत्ति यह है कि उनके पास एक बफर है, जो लिनक्स पर 40 9 6 बाइट्स है, और वे वास्तव में एक है लिनक्स स्रोत कोड में परिभाषित फाइल सिस्टम ! वे डेटा को पास करने के लिए बस एक वस्तु नहीं हैं, वे स्वयं एक डेटास्ट्रक्चर हैं! वास्तव में, क्योंकि वहां पाइपफ फाइल सिस्टम मौजूद है, जो पाइप और एफआईएफओ दोनों का प्रबंधन करता है, पाइप में एक इनोड होता है उनके संबंधित फाइल सिस्टम पर संख्या:

# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/  | cat  
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'

लिनक्स पाइप पर अनियंत्रित हैं, बस पुनर्निर्देशन की तरह। कुछ यूनिक्स-जैसी कार्यान्वयन पर - द्वि-दिशात्मक पाइप हैं। हालांकि खोल स्क्रिप्टिंग के जादू के साथ, आप कर सकते हैं लिनक्स पर द्वि-दिशात्मक पाइप भी।

यह भी देखें:


3
2017-09-12 09:26





अन्य उत्तरों में जोड़ने के लिए, सूक्ष्म अर्थपूर्ण अंतर भी हैं - उदा। पाइप रीडायरेक्ट की तुलना में अधिक आसानी से बंद:

seq 5 | (head -n1; head -n1)                # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5   # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1)   # 1 and 2

पहले उदाहरण में, जब पहली बार कॉल करें head खत्म होता है, यह पाइप बंद करता है, और seq समाप्त हो जाता है, इसलिए दूसरे के लिए कोई इनपुट उपलब्ध नहीं है head

दूसरे उदाहरण में, सिर पहली पंक्ति का उपभोग करता है, लेकिन जब यह बंद हो जाता है stdin  पाइप, फाइल अगली कॉल के लिए उपयोग के लिए खुला रहता है।

तीसरा उदाहरण दिखाता है कि अगर हम उपयोग करते हैं read पाइप बंद करने से बचने के लिए यह अभी भी उपप्रोसेसर के भीतर उपलब्ध है।

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

अनुलेख यदि आप उन उदाहरणों के बारे में उत्सुक हैं और / या आश्चर्यचकित हैं, तो आप आगे का उपयोग करके खुदाई कर सकते हैं trap यह देखने के लिए कि प्रक्रिया कैसे हल होती है, E.g:

(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))

कभी-कभी पहली प्रक्रिया पहले बंद हो जाती है 1 मुद्रित किया जाता है, कभी-कभी बाद में।

मुझे यह भी उपयोग करना दिलचस्प लगता है exec <&- पाइप के व्यवहार को अनुमानित करने के लिए पुनर्निर्देशन से स्ट्रीम को बंद करने के लिए (हालांकि त्रुटि के बावजूद):

seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`

2
2018-06-05 00:54



"जब सिर पर पहला कॉल खत्म होता है, तो यह पाइप बंद कर देता है" यह वास्तव में दो कारणों से गलत है। एक, (हेड-एन 1; हेड-एन 1) दो कमांड के साथ सबहेल है, जिनमें से प्रत्येक को पाइप के अंत में वर्णक 0 के रूप में पढ़ा जाता है, और इस प्रकार सबहेल होता है और प्रत्येक कमांड में फ़ाइल डिस्क्रिप्टर खुला होता है। दूसरा कारण, आप देख सकते हैं कि strace -f bash -c 'seq 5 | के साथ (हेड-एन 1; हेड-एन 1) '। तो पहला सिर केवल फाइल डिस्क्रिप्टर की अपनी प्रति बंद करता है - Sergiy Kolodyazhnyy
तीसरा उदाहरण भी गलत है, क्योंकि read केवल पहली पंक्ति का उपभोग करता है (यह एक बाइट है 1 और नई लाइन)। seq कुल 10 बाइट्स में भेजा गया (5 संख्याएं और 5 न्यूलाइन)। तो पाइप बफर में 8 बाइट शेष हैं, और यही कारण है कि दूसरा head काम करता है - अभी भी पाइप बफर में डेटा उपलब्ध है। बीटीडब्ल्यू, सिर केवल तभी निकलता है जब 0 बाइट्स पढ़ते हैं, जैसा कि अंदर है head /dev/null - Sergiy Kolodyazhnyy
स्पष्टीकरण के लिए धन्यवाद। क्या मैं सही ढंग से समझ रहा हूं seq 5 | (head -n1; head -n1) पहला कॉल पाइप खाली करता है, इसलिए यह अभी भी एक खुले राज्य में मौजूद है लेकिन दूसरे कॉल के लिए कोई डेटा नहीं है head? तो पाइप और रीडायरेक्ट के बीच व्यवहार में अंतर यह है कि सिर पाइप से बाहर सभी डेटा खींचता है, लेकिन फाइल हैंडल से केवल 2 लाइनें खींचती है? - Julian de Bhal
यह सही है। और यह ऐसा कुछ है जिसे देखा जा सकता है strace कमांड मैंने पहली टिप्पणी में दिया था। पुनर्निर्देशन के साथ, टीएमपी फ़ाइल डिस्क पर है जो इसे खोजने योग्य बनाता है (क्योंकि वे उपयोग करते हैं lseek() syscall - आदेश पहले बाइट से फ़ाइल के आसपास कूद सकते हैं हालांकि वे चाहते हैं। लेकिन पाइप अनुक्रमिक हैं और तलाश योग्य नहीं हैं। तो सिर के लिए अपना काम करने का एकमात्र तरीका सबकुछ पहले पढ़ना है, या यदि फ़ाइल बड़ी है - तो इसमें से कुछ को रैम के माध्यम से मैप करें mmap() कहते हैं। मैंने एक बार अपना खुद का किया tail पायथन में, और एक ही समस्या में भाग गया। - Sergiy Kolodyazhnyy
यह भी याद रखना महत्वपूर्ण है कि पाइप (फ़ाइल डिस्क्रिप्टर) का पढ़ना अंत पहले सबहेल को दिया जाता है (...), और सबहेल प्रत्येक कमांड में अपने स्वयं के stdin की प्रति बना देगा (...)। तो वे तकनीकी रूप से एक ही वस्तु से पढ़ रहे हैं। प्रथम head  लगता है कि यह अपने स्वयं के stdin से पढ़ रहा है। दूसरा head लगता है कि इसका अपना स्टडीन है। लेकिन हकीकत में उनके एफडी # 1 (stdin) सिर्फ एक ही एफडी की प्रति है, जो पाइप के अंत में पढ़ा जाता है। इसके अलावा, मैंने एक उत्तर पोस्ट किया है, इसलिए शायद यह चीजों को स्पष्ट करने में मदद करेगा। - Sergiy Kolodyazhnyy


मैंने आज सी में इसके साथ एक समस्या आई है। अनिवार्य रूप से पाइप के पास रीडायरेक्ट करने के लिए अलग-अलग अर्थशास्त्र भी होते हैं, यहां तक ​​कि भेजे जाने पर भी stdin। वास्तव में मुझे लगता है कि अंतरों को देखते हुए, पाइप को कहीं और जाना चाहिए stdin, ताकि stdin और इसे कॉल करने देता है stdpipe (मनमाना अंतर बनाने के लिए) विभिन्न तरीकों से संभाला जा सकता है।

इस पर विचार करो। एक प्रोग्राम आउटपुट को दूसरे पर पाइप करते समय fstat लगता है कि शून्य के रूप में शून्य वापस आती है st_size के बावजूद ls -lha /proc/{PID}/fd दिखा रहा है कि एक फाइल है। फ़ाइल को रीडायरेक्ट करते समय यह मामला नहीं है (कम से कम डेबियन पर wheezy, stretch तथा jessie वेनिला और उबंटू 14.04, 16.04 वनीला।

अगर तुम cat /proc/{PID}/fd/0 एक पुनर्निर्देशन के साथ आप जितनी बार चाहें उतनी बार पढ़ने के लिए दोहराने में सक्षम होंगे। यदि आप इसे पाइप के साथ करते हैं तो आप देखेंगे कि दूसरी बार जब आप लगातार कार्य चलाते हैं, तो आपको एक ही आउटपुट नहीं मिलता है।


1
2017-10-26 16:17