नोट: उत्तर इन तंत्रों के बारे में मेरी अपनी समझ को दर्शाता है, इस साइट पर साथियों द्वारा जवाबों के अनुसंधान और पढ़ने पर संचित है और 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 आंतरिक ज्ञापन द्वारा मैल्कम डगलस मेक्लोरी , समय था जब वे काम कर रहे थे पर मॉलटिक्स ऑपरेटिंग सिस्टम । उद्धरण:
संक्षेप में अपनी मजबूत चिंताओं को रखने के लिए:
- हमारे पास बगीचे की नली जैसे कार्यक्रमों को जोड़ने के कुछ तरीके होने चाहिए - दूसरे खंड में पेंच जब यह तब हो जाता है जब किसी अन्य तरीके से डेटा की मालिश करना आवश्यक हो जाता है। आईओ का भी यही तरीका है।
यह स्पष्ट है कि उस समय कार्यक्रम डिस्क पर लिखने में सक्षम थे, हालांकि आउटपुट बड़े होने पर अक्षम था। यूनिक्स पाइपलाइन वीडियो में ब्रायन कर्निघन के स्पष्टीकरण को उद्धृत करने के लिए :
सबसे पहले, आपको एक बड़े पैमाने पर बड़े कार्यक्रम को लिखने की ज़रूरत नहीं है - आपको मौजूदा छोटे कार्यक्रम मिल गए हैं जो पहले से ही नौकरी के कुछ हिस्सों को कर सकते हैं ... एक और यह है कि यह संभव है कि आपके द्वारा अनुमानित डेटा की मात्रा फिट न हो आपने इसे एक फ़ाइल में संग्रहीत किया है ... क्योंकि याद रखें, हम उन दिनों में वापस आ गए हैं जब इन चीजों पर डिस्क थी, यदि आप भाग्यशाली थे, एक मेगाबाइट या डेटा के दो ... तो पाइपलाइन को कभी भी पूरे आउटपुट को तुरंत नहीं करना पड़ा ।
इस प्रकार वैचारिक अंतर स्पष्ट है: पाइप एक दूसरे से बात करने वाले कार्यक्रमों को बनाने का एक तंत्र है। पुनर्निर्देशन - मूल स्तर पर फ़ाइल करने के लिए लिखने का तरीका है। दोनों मामलों में, शेल इन दो चीजों को आसान बनाता है, लेकिन हुड के नीचे, वहाँ बहुत कुछ चल रहा है।
गहराई तक जा रहे हैं: खोल के सिसकल्स और आंतरिक कामकाज
हम फ़ाइल डिस्क्रिप्टर की धारणा से शुरू करते हैं । फ़ाइल डिस्क्रिप्टर मूल रूप से एक खुली फ़ाइल (चाहे वह डिस्क पर कोई फ़ाइल हो, या मेमोरी या अनाम फ़ाइल में) का वर्णन करता है, जिसे पूर्णांक संख्या द्वारा दर्शाया जाता है। दो मानक डेटा धाराएँ (स्टडिन, स्टडआउट, स्टैडर) क्रमशः फाइल डिस्क्रिप्टर 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]'
रिडायरेक्शन की तरह ही लिनक्स पाइप यूनी-दिशात्मक हैं। कुछ यूनिक्स जैसे कार्यान्वयन पर - द्वि-दिशात्मक पाइप हैं। यद्यपि शेल स्क्रिप्टिंग के जादू के साथ, आप लिनक्स पर भी द्वि-दिशात्मक पाइप बना सकते हैं ।
यह सभी देखें:
thing1 > temp_file && thing2 < temp_file
पाइप के साथ और अधिक आसान करने का उल्लेख किया । लेकिन>
ऐसा करने के लिए ऑपरेटर का पुनः उपयोग क्यों नहीं किया जाता है, जैसेthing1 > thing2
कि कमांड के लिएthing1
औरthing2
? एक अतिरिक्त ऑपरेटर क्यों|
?