किस क्रम में शेल कमांड निष्पादित करता है और पुनर्निर्देशन को स्ट्रीम करता है?


32

मैं दोनों रीडायरेक्ट करने के लिए कोशिश कर रहा था stdoutऔर stderrएक फाइल करने के लिए आज, और मैं इस पार आया था:

<command> > file.txt 2>&1

यह स्पष्ट रूप stderrसे stdoutपहले पर पुनर्निर्देशित करता है , और फिर परिणाम stdoutको पुनर्निर्देशित किया जाता है file.txt

हालांकि, आदेश क्यों नहीं है <command> 2>&1 > file.txt? एक स्वाभाविक रूप से इसे पढ़ेंगे (पहले से बाएं से दाएं को निष्पादित करते हुए) कमांड को पहले निष्पादित किया stderrजा रहा है stdout, फिर रिडायरेक्ट किया जा रहा है , जिसके परिणामस्वरूप stdoutलिखा जा रहा है file.txt। लेकिन ऊपर केवल stderrस्क्रीन पर रीडायरेक्ट होता है।

शेल दोनों कमांड की व्याख्या कैसे करता है?


7
TLCL यह कहता है कि "पहले हम मानक आउटपुट को फाइल में रीडायरेक्ट करते हैं, और फिर हम डिस्क्रिप्टर वन (स्टैंडर्ड आउटपुट) को फाइल करने के लिए डिस्क्रिप्टर 2 (स्टैंडर्ड एरर) को रीडायरेक्ट करते हैं और" रिडायरेक्शन का क्रम महत्वपूर्ण होता है। मानक आउटपुट को पुनर्निर्देशित करने के बाद हमेशा होना चाहिए या यह काम नहीं करता है "
Zanna

@Zanna: हाँ, मेरा सवाल ठीक टीएलसीएल में पढ़ने से आया है! :) मुझे यह जानने में दिलचस्पी है कि यह काम क्यों नहीं करेगा, अर्थात, शेल सामान्य रूप से कमांड की व्याख्या कैसे करता है।
ट्रेन हार्टनेट

ठीक है, आपके सवाल में आप कहते हैं कि "यह जाहिरा तौर पर स्टडआउट को पहले से ही पुनर्निर्देशित करता है ..." - टीएलसीएल से मुझे जो समझ में आता है, वह यह है कि शेल फाइल को स्टडआउट भेजता है और फिर स्टडआउट को (फाइल को)। मेरी व्याख्या यह है कि अगर आप stdout को stderr भेजते हैं, तो इसे टर्मिनल में प्रदर्शित किया जाएगा, और बाद में stdout के पुनर्निर्देशन में stderr शामिल नहीं होगा (यानी stdout के पुनर्निर्देशन से पहले स्क्रीन पर stderr का पुनर्निर्देशन पूरा होता है?)
Zanna

7
मुझे पता है कि यह कहने के लिए एक पुराने जमाने की बात है, लेकिन आपका खोल एक मैनुअल के साथ आता है जो इन चीजों को समझाता है - जैसे मैनुअल में पुनर्निर्देशनbash । वैसे, पुनर्निर्देशन आज्ञा नहीं हैं।
रेनियर पोस्ट

पुनर्निर्देशन स्थापित होने से पहले कमांड को निष्पादित नहीं किया जा सकता है: execv-family syscall को वास्तव में कमांड से उपप्रकार को हाथ लगाने के लिए आमंत्रित किया जाता है, तो शेल तब लूप से बाहर हो जाता है (अब उस प्रक्रिया में कोड निष्पादित नहीं होता है ) और उस बिंदु से क्या होता है इसे नियंत्रित करने का कोई संभव तरीका नहीं है; इस प्रकार निष्पादन शुरू होने से पहले सभी को पुनर्निर्देशित किया जाना चाहिए , जबकि शेल में स्वयं की एक प्रति है जो इस प्रक्रिया में चल रही है कि यह fork()आपकी कमांड को चलाने के लिए एड है।
चार्ल्स डफी

जवाबों:


41

जब आप <command> 2>&1 > file.txtstderr चलाते हैं 2>&1, तो stdout वर्तमान में आपके टर्मिनल पर कहाँ जाता है। उसके बाद, stdout फ़ाइल द्वारा पुनर्निर्देशित किया जाता है >, लेकिन इसके साथ stderr को पुनर्निर्देशित नहीं किया जाता है, इसलिए टर्मिनल आउटपुट पर रहता है।

<command> > file.txt 2>&1Stdout के साथ पहले फ़ाइल पर पुनर्निर्देशित किया जाता है >, फिर 2>&1stdout जहाँ stdout जा रहा है, फ़ाइल को पुनर्निर्देशित करता है।

यह शुरू करने के लिए काउंटर सहज ज्ञान युक्त लग सकता है, लेकिन जब आप इस तरह से पुनर्निर्देशन के बारे में सोचते हैं, और याद रखें कि उन्हें बाएं से दाएं संसाधित किया जाता है तो यह बहुत अधिक समझ में आता है।


यह समझ में आता है अगर आप फ़ाइल-डिस्क्रिप्टर और "डुप / fdreopen" कॉल के संदर्भ में सोचते हैं, जिसे बाएं से दाएं से निष्पादित किया जा रहा है
मार्क के कोवान

20

यदि आप इसे ट्रेस करते हैं तो यह समझ में आ सकता है।

शुरुआत में, stderr और stdout एक ही चीज़ पर जाते हैं (आमतौर पर टर्मिनल, जिसे मैं यहां कहता हूं pts):

fd/0 -> pts
fd/1 -> pts
fd/2 -> pts

मैं उनके फ़ाइल डिस्क्रिप्टर नंबरों द्वारा स्टड, स्टडआउट और स्टेडर का उल्लेख कर रहा हूं : वे क्रमशः फाइल डिस्क्रिप्टर 0, 1 और 2 हैं।

अब, पुनर्निर्देशन के पहले सेट में, हमारे पास > file.txtऔर है 2>&1

इसलिए:

  1. > file.txt: fd/1अब जाता है file.txt। साथ >, 1निहित फ़ाइल वर्णनकर्ता जब कुछ भी निर्दिष्ट किया जाता है, तो यह है 1>file.txt:

    fd/0 -> pts
    fd/1 -> file.txt
    fd/2 -> pts
  2. 2>&1: fd/2अब जाता है जहाँ भी fd/1 वर्तमान में जाता है:

    fd/0 -> pts
    fd/1 -> file.txt
    fd/2 -> file.txt

दूसरी ओर, 2>&1 > file.txtक्रम उलट रहा है:

  1. 2>&1: fd/2अब जाता है जहाँ भी fd/1जाता है, जिसका अर्थ है कि कुछ भी नहीं बदलता है:

    fd/0 -> pts
    fd/1 -> pts
    fd/2 -> pts
  2. > file.txt: fd/1अब जाता है file.txt:

    fd/0 -> pts
    fd/1 -> file.txt
    fd/2 -> pts

महत्वपूर्ण बिंदु यह है कि पुनर्निर्देशन का मतलब यह नहीं है कि पुनर्निर्देशित फ़ाइल डिस्क्रिप्टर लक्ष्य फ़ाइल डिस्क्रिप्टर के भविष्य के सभी परिवर्तनों का पालन करेगा; यह केवल वर्तमान स्थिति पर ले जाएगा ।


धन्यवाद, यह एक अधिक प्राकृतिक व्याख्या की तरह लगता है! :) आपने 2.दूसरे भाग में हालांकि थोड़ा सा टाइपो बनाया ; fd/1 -> file.txtऔर नहीं fd/2 -> file.txt
ट्रेन हार्टनेट

11

मुझे लगता है कि यह सोचने में मदद करता है कि शेल पहले बाईं ओर पुनर्निर्देशन स्थापित करेगा, और अगला पुनर्निर्देशन स्थापित करने से पहले इसे पूरा करेगा।

विलियम शॉटस द्वारा लिनक्स कमांड लाइन का कहना है

पहले हम मानक आउटपुट को फाइल में रीडायरेक्ट करते हैं, और फिर हम डिस्क्रिप्टर एक (स्टैंडर्ड आउटपुट) को फाइल करने के लिए डिस्क्रिप्टर 2 (स्टैंडर्ड एरर) को रीडायरेक्ट करते हैं।

यह समझ में आता है, लेकिन फिर

ध्यान दें कि पुनर्निर्देश का क्रम महत्वपूर्ण है। मानक त्रुटि के पुनर्निर्देशन को हमेशा मानक उत्पादन को पुनर्निर्देशित करने के बाद होना चाहिए या यह काम नहीं करता है

लेकिन वास्तव में, हम उसी प्रभाव के साथ एक फ़ाइल के लिए stderr को पुनर्निर्देशित करने के बाद stdout को पुनः निर्देशित कर सकते हैं

$ uname -r 2>/dev/null 1>&2
$ 

तो, command > file 2>&1शेल एक फाइल को stdout भेजता है, फिर stdout को stderr भेजता है (जो फ़ाइल में भेजा जा रहा है)। जबकि, command 2>&1 > fileशेल में पहले stdout पर stderr को रीडायरेक्ट किया जाता है (अर्थात इसे टर्मिनल में प्रदर्शित किया जाता है जहां stdout सामान्य रूप से जाता है) और फिर बाद में, फ़ाइल पर stdout को रीडायरेक्ट करता है। TLCL यह कहकर गुमराह कर रही है कि हमें पहले stdout को रीडायरेक्ट करना होगा: चूंकि हम पहले किसी फ़ाइल में stderr को रीडायरेक्ट कर सकते हैं और उसके बाद stdout को भेज सकते हैं। हम क्या नहीं कर सकते हैं, एक फ़ाइल पर पुनर्निर्देशन से पहले stderr या इसके विपरीत करने के लिए रीडायरेक्ट है। एक और उदाहरण

$ strace uname -r 1>&2 2> /dev/null 
4.8.0-30-generic

हमें लगता है कि यह stdout को stderr के समान स्थान पर डिस्पोज़ करेगा, लेकिन ऐसा नहीं है, यह stdout को stderr (स्क्रीन) पर पहले रीडायरेक्ट करता है, और उसके बाद ही stderr को रीडायरेक्ट करता है, जब हमने इसे दूसरे तरीके से आज़माया था ...

मुझे उम्मीद है कि यह थोड़ा प्रकाश लाता है ...


इतना अधिक वाक्पटु!
एरनिकल

आह, अब मैं समझ गया! बहुत बहुत धन्यवाद, @Zanna और @Arronical! बस मेरी कमांड लाइन यात्रा शुरू हो रही है। :)
ट्रेन हार्टनेट

@TrainHeartnet यह एक खुशी है! आशा है कि आप इसे मुझे जितना पसंद कर रहे हैं: D
Zanna

@Zanna: वास्तव में, मैं हूँ! : D
ट्रेन हार्टनेट

2
@TrainHeartnet कोई चिंता नहीं, निराशा और खुशी की दुनिया का इंतजार कर रहा है!
द्रोणिका

10

आपको पहले से ही कुछ बहुत अच्छे जवाब मिले। मुझे इस बात पर जोर देना चाहिए कि यहां दो अलग-अलग अवधारणाएं शामिल हैं, जिनमें से समझ काफी मदद करती है:

बैकग्राउंड: फाइल डिस्क्रिप्टर बनाम फाइल टेबल

आपकी फ़ाइल डिस्क्रिप्टर सिर्फ एक संख्या 0 ... n है, जो आपकी प्रक्रिया में फ़ाइल डिस्क्रिप्टर तालिका में अनुक्रमणिका है। कन्वेंशन द्वारा, STDIN = 0, STDOUT = 1, STDERR = 2 (ध्यान दें कि यहां के शब्द STDINआदि) सिर्फ प्रतीक / मैक्रोज़ हैं जो कुछ प्रोग्रामिंग भाषाओं और मैन पेजों में कन्वेंशन द्वारा उपयोग किए जाते हैं, एसटीडीआईएन के लिए एक वास्तविक "ऑब्जेक्ट" नहीं है; इस चर्चा का उद्देश्य, STDIN है 0, आदि)।

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

इसलिए जब आप उपयोग करते हैं >या <अपने शेल में होते हैं, तो आप किसी अन्य चीज़ को इंगित करने के लिए संबंधित फ़ाइल डिस्क्रिप्टर के पॉइंटर को बदल देते हैं। सिंटैक्स 2>&1बस वर्णनकर्ता 2 को इंगित करता है जहाँ भी 1 अंक। > file.txtबस file.txtलिखने के लिए खुलता है और STDOUT (फ़ाइल डिक्रिप्टर 1) को उस ओर इंगित करने देता है।

अन्य अच्छाइयाँ हैं, उदाहरण के लिए 2>(xxx) (जैसे: एक नई प्रक्रिया चल रही है xxx, एक पाइप बनाएँ, पाइप के रीडिंग एंड के फाइल डिस्क्रिप्टर 0 को कनेक्ट करें और मूल प्रक्रिया के फाइल डिस्क्रिप्टर 2 को राइटिंग एंड के राइटिंग सिरे से कनेक्ट करें। पाइप)।

यह आपके शेल के अलावा अन्य सॉफ़्टवेयर में "फ़ाइल हैंडल मैजिक" का आधार भी है। उदाहरण के लिए, आप अपनी पर्ल स्क्रिप्ट में, dupSTDOUT फ़ाइल डिस्क्रिप्टर को दूसरे (अस्थायी) में से एक में लाइसेंस कर सकते हैं, फिर STDOUT को एक नई बनाई गई अस्थायी फ़ाइल में फिर से खोलें। इस बिंदु से, आपकी अपनी पर्ल स्क्रिप्ट से सभी STDOUT आउटपुट औरsystem() उस स्क्रिप्ट की सभी कॉल उस अस्थायी फ़ाइल में समाप्त हो जाएंगी। जब किया जाता है, तो आप dupउस अस्थायी विवरणक को फिर से भेज सकते हैं जिसे आपने इसे सहेजा था, और इसे पूर्व में, जैसा कि सभी पहले से बता चुके हैं। आप उस अस्थायी डिस्क्रिप्टर को इस बीच भी लिख सकते हैं, इसलिए जब आपका वास्तविक STDOUT आउटपुट अस्थायी फ़ाइल में जाता है, तब भी आप वास्तव में वास्तविक STDOUT (आमतौर पर, उपयोगकर्ता) को सामान आउटपुट कर सकते हैं ।

उत्तर

अपने प्रश्न के ऊपर दी गई पृष्ठभूमि की जानकारी को लागू करने के लिए:

किस क्रम में शेल कमांड निष्पादित करता है और पुनर्निर्देशन को स्ट्रीम करता है?

बाएं से दाएं।

<command> > file.txt 2>&1

  1. fork एक नई प्रक्रिया बंद।
  2. file.txtफ़ाइल डिस्क्रिप्टर 1 (STDOUT) में इसके पॉइंटर को खोलें और स्टोर करें।
  3. बिंदु STDERR (फ़ाइल डिस्क्रिप्टर 2) जो भी हो fd 1 को अभी अंक (जो फिर से पहले से ही खोला गया file.txtहै)।
  4. exec <command>

यह स्पष्ट रूप से पहले stdout करने के लिए stderr को पुनर्निर्देशित करता है, और फिर परिणामी stdout को file.txt पर पुनर्निर्देशित किया जाता है।

यह समझ में आता है अगर वहाँ केवल एक मेज थे , लेकिन जैसा कि ऊपर बताया गया है कि दो हैं। फ़ाइल डिस्क्रिप्टर एक-दूसरे को पुनरावृत्ति की ओर इशारा नहीं कर रहे हैं, यह सोचने के लिए कोई मतलब नहीं है "STDERR को STREOUT"। सही विचार "बिंदु STDERR है जहाँ भी बिंदुओं के लिए"। यदि आप STDOUT को बाद में बदलते हैं, तो STDERR जहां रहता है, वह जादुई रूप से STDOUT में और परिवर्तन के साथ नहीं जाता है।


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

3

आदेश को दाईं ओर छोड़ दिया गया है। बैश का मैनुअल पहले से ही कवर किया गया है जो आप पूछते हैं। REDIRECTIONमैनुअल के भाग से उद्धरण :

   Redirections  are  processed  in  the
   order they appear, from left to right.

और कुछ पंक्तियाँ बाद में:

   Note that the order of redirections is signifi
   cant.  For example, the command

          ls > dirlist 2>&1

   directs both standard output and standard error
   to the file dirlist, while the command

          ls 2>&1 > dirlist

   directs   only  the  standard  output  to  file
   dirlist, because the standard error was  dupli
   cated from the standard output before the stan
   dard output was redirected to dirlist.

यह ध्यान रखना महत्वपूर्ण है कि पुनर्निर्देशन से पहले किसी भी आदेश को चलाने से पहले हल किया जाता है! Https://askubuntu.com/a/728386/295286 देखें


3

यह हमेशा दाएं से बाएं होता है ... सिवाय कब

जैसे गणित में हम गुणा करने के अलावा दाएं से बाएं करते हैं और विभाजन और जोड़-घटाव से पहले किया जाता है, कोष्ठक (+ -) के अंदर संचालन को छोड़कर गुणा और विभाजन से पहले किया जाएगा।

बैश शुरुआती गाइड के अनुसार यहां ( बैश बिगिनर्स गाइड ) पहले (पहले बाएं से दाएं) आने वाले पदानुक्रम के 8 आदेश हैं:

  1. ब्रेस विस्तार "{}"
  2. टिल्ड विस्तार "~"
  3. शेल पैरामीटर और चर अभिव्यक्ति "$"
  4. कमांड प्रतिस्थापन "$ (कमांड)"
  5. अंकगणितीय अभिव्यक्ति "$ ((प्रदर्शनी))"
  6. प्रक्रिया प्रतिस्थापन हम यहाँ क्या बात कर रहे हैं "<(LIST)" या "> (LIST)"
  7. शब्द विभाजन "" <स्थान> <टैब> <newline> '"
  8. फ़ाइल का नाम विस्तार "*", "?", आदि।

इसलिए यह हमेशा दाएं से बाएं होता है ... सिवाय कब ...


1
स्पष्ट होना: प्रक्रिया प्रतिस्थापन और पुनर्निर्देशन दो स्वतंत्र संचालन हैं। आप एक दूसरे के बिना कर सकते हैं। प्रक्रिया प्रतिस्थापन होने का मतलब यह नहीं है कि बैश उस क्रम को बदलने का फैसला करता है जिसमें वह पुनर्निर्देशन की प्रक्रिया करता है
muru
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.