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


204

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

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

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

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

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

जवाबों:


223

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

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

उदाहरण: 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

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

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

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

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

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


धन्यवाद। आपने thing1 > temp_file && thing2 < temp_fileपाइप के साथ और अधिक आसान करने का उल्लेख किया । लेकिन >ऐसा करने के लिए ऑपरेटर का पुनः उपयोग क्यों नहीं किया जाता है, जैसे thing1 > thing2कि कमांड के लिए thing1और thing2? एक अतिरिक्त ऑपरेटर क्यों |?
जॉन थ्रीपवुड 13

1
"आउटपुट लें और इसे एक फ़ाइल में लिखें" एक अलग क्रिया है "आउटपुट लें और इसे एक अलग प्रोग्राम में पास करें"। मैं अपने उत्तर में और अधिक विचारों को संपादित करूँगा ...
डेविड वनिल

1
@JohnThreepwood उनके अलग-अलग अर्थ हैं। क्या होगा अगर मैं lessउदाहरण के लिए, नाम की फ़ाइल में कुछ पुनर्निर्देशित करना चाहता हूं ? thing | lessऔर thing > lessवे पूरी तरह से अलग हैं, क्योंकि वे अलग-अलग काम करते हैं। आप जो प्रस्ताव करेंगे वह एक अस्पष्टता पैदा करेगा।
डार्कहॉग

क्या यह कहना सही है कि "thing1> temp_file" केवल "thing1 | tee temp_file" के लिए सिंटैक्टिक चीनी है? टी के बारे में पता लगाने के बाद से मैं लगभग कभी भी रीडायरेक्ट का उपयोग नहीं करता हूं।
श्रीधर सरनोबत

2
@ श्रीधर-सरनोबत नहीं, teeआदेश कुछ अलग करता है। teeस्क्रीन ( stdout) और फ़ाइल दोनों के लिए आउटपुट लिखता है । रीडायरेक्ट केवल फ़ाइल करता है ।
डेविड वन

22

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


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

13

यूनिक्स और लिनक्स सिस्टम एडमिनिस्ट्रेशन हैंडबुक से:

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

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

पाइप्स

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

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


12

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

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

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

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

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

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


3

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

  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शिशु से लाइनों को पढ़ेगा, फिर एक साथ प्रत्येक पंक्ति को लिखकर दूसरी पंक्ति में भेजेगा cat

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

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

grep blah <<<`ls`

के रूप में एक ही परिणाम देगा

ls | grep blah

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


1
नाइटपिक: पुनर्निर्देशन में मामलों को क्रमबद्ध करें यदि आप किसी अन्य के लिए एक एफडी को 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
मुरु

2
रीडायरेक्ट वास्तव में कार्यक्रम का तर्क नहीं है। प्रोग्राम को पता नहीं होगा या परवाह नहीं है कि उसका आउटपुट कहां जाता है (या इनपुट कहां से आता है)। यह कार्यक्रम को चलाने से पहले चीजों को व्यवस्थित करने का तरीका बता रहा है।
एलोइस महदल

3

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

टी एल; डॉ

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

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

परिचय

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

द ऑरिजिन्स ऑफ़ रिडायरेक्शन्स एंड पाइप्स

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

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

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

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

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

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

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

हम फ़ाइल डिस्क्रिप्टर की धारणा से शुरू करते हैं । फ़ाइल डिस्क्रिप्टर मूल रूप से एक खुली फ़ाइल (चाहे वह डिस्क पर कोई फ़ाइल हो, या मेमोरी या अनाम फ़ाइल में) का वर्णन करता है, जिसे पूर्णांक संख्या द्वारा दर्शाया जाता है। दो मानक डेटा धाराएँ (स्टडिन, स्टडआउट, स्टैडर) क्रमशः फाइल डिस्क्रिप्टर 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तो आपके पास दो समान फाइलें होंगी, लेकिन आप उन्हें स्वतंत्र रूप से जोड़-तोड़ कर सकते हैं? यही थोड़े यहाँ हो रहा है। अक्सर आप देख सकते हैं कि दौड़ने से पहले, एक कॉपी फाइल डिस्क्रिप्टर # 1 बनाने के लिए bashकरेगा dup(1,10)जो 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। इसका वास्तविक कार्य उदाहरण पूंछ है

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

# 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]'

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

यह सभी देखें:


2

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

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पाइप को बंद करने से बचने के लिए उपयोग करते हैं तो यह अभी भी उपप्रकार के भीतर उपलब्ध है।

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

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

(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`

"जब पहला कॉल हेड खत्म होता है, तो यह पाइप बंद कर देता है" यह वास्तव में दो कारणों से गलत है। एक, (head -n1; head -n1) दो कमांड्स के साथ सब-सब्सक्राइब किया जाता है, जिनमें से प्रत्येक में पाइप के अंत को विवरणकर्ता 0 के रूप में पढ़ा जाता है, और इस तरह सब-टाइप और प्रत्येक कमांड में वह फाइल डिस्क्रिप्टर खुला होता है। दूसरा कारण, आप देख सकते हैं कि स्ट्रेस -f बैश -c 'seq 5 के साथ | (हेड-एन 1; हेड-एन 1) '। तो पहले सिर फाइल डिस्क्रिप्टर की केवल इसकी कॉपी बंद कर देता है
सर्जि कोलोडियाज़नी

तीसरा उदाहरण भी गलत है, क्योंकि readकेवल पहली पंक्ति (यह एक बाइट के लिए 1और न्यूलाइन है) का उपभोग करता है । seqकुल 10 बाइट्स (5 नंबर और 5 न्यूलाइन्स) भेजे गए। तो पाइप बफर में 8 बाइट्स शेष हैं, और यही कारण है कि दूसरा headकाम करता है - पाइप बफर में अभी भी डेटा उपलब्ध है। Btw, सिर बाहर निकलता है अगर केवल 0 बाइट्स पढ़े जाते हैं, थोड़े के रूप मेंhead /dev/null
Sergiy Kolodyazhnyy

स्पष्टीकरण के लिए धन्यवाद। क्या मैं सही ढंग से समझ रहा हूं कि seq 5 | (head -n1; head -n1)पहली कॉल में पाइप खाली हो जाता है, इसलिए यह अभी भी एक खुली स्थिति में मौजूद है, लेकिन दूसरी कॉल के लिए कोई डेटा नहीं है head? तो पाइप और रिडायरेक्ट के बीच व्यवहार में अंतर इसलिए है क्योंकि सिर पाइप से सभी डेटा को खींचता है, लेकिन फ़ाइल हैंडल से केवल 2 पंक्तियाँ?
जूलियन डी भाल

यह सही है। और यह कुछ ऐसा है जिसे straceमैं पहली टिप्पणी में दी गई कमांड के साथ देख सकता हूं। पुनर्निर्देशन के साथ, tmp फ़ाइल डिस्क पर होती है, जो इसे खोज योग्य बनाती है (क्योंकि वे lseek()syscall का उपयोग करते हैं - कमांड फाइल के चारों ओर से पहली बाइट तक छलांग लगा सकते हैं, हालांकि वे चाहते हैं। लेकिन पाइप अनुक्रमिक हैं और खोज योग्य नहीं हैं। इसलिए सिर से ऐसा करने का एकमात्र तरीका है। नौकरी को पहले सब कुछ पढ़ना है, या अगर फ़ाइल बड़ी है - mmap()कॉल के माध्यम से रैम में से कुछ को मैप करें। मैंने एक बार tailपायथन में अपना खुद का काम किया था , और ठीक उसी समस्या में भाग गया।
सेर्गिए कोलोडियाज़नी

यह याद रखना भी महत्वपूर्ण है कि पाइप का अंतिम छोर (फाइल डिस्क्रिप्टर) पहले सब्स्क्राइब करने के लिए दिया गया है (...), और सब-डिमांड प्रत्येक स्टाइन की कॉपी को अंदर प्रत्येक कमांड को बनाएगी (...)। इसलिए वे तकनीकी रूप से एक ही वस्तु से पढ़ते हैं। पहले head सोचता है कि यह स्वयं की स्टड से पढ़ रहा है। दूसरा headसोचता है कि इसकी अपनी गति है। लेकिन वास्तव में उनकी fd # 1 (स्टडिन) केवल उसी fd की प्रति है, जिसे पाइप का अंत पढ़ा जाता है। इसके अलावा, मैंने एक जवाब पोस्ट किया है, इसलिए शायद यह चीजों को स्पष्ट करने में मदद करेगा।
सर्गी कोलोडियाज़नी

1

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

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

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

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