फ़ाइल डिस्क्रिप्टर को स्थानांतरित करने के लिए व्यावहारिक उपयोग


16

बैश मैन पेज के अनुसार:

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

   [n]<&digit-

फ़ाइल डिस्क्रिप्टर digitफ़ाइल डिस्क्रिप्टर n, या मानक इनपुट (फ़ाइल डिस्क्रिप्टर 0) nको निर्दिष्ट नहीं होने पर ले जाता है। digitके बाद बंद कर दिया जा रहा है closed दुपट्टे li के लिए n

फ़ाइल डिस्क्रिप्टर को किसी दूसरे पर "स्थानांतरित" करने का क्या मतलब है? इस तरह के अभ्यास के लिए विशिष्ट परिस्थितियां क्या हैं?

जवाबों:


14

3>&4-kash93 एक्सटेंशन को भी bash द्वारा सपोर्ट किया गया है और जो 3>&4 4>&-कि 3 के लिए छोटा है , जो कि 4 के लिए इस्तेमाल किया जाता है, और 4 अब बंद हो गया है, इसलिए 4 से जो इंगित किया गया था, वह अब 3 हो गया है।

विशिष्ट उपयोग उन मामलों में होगा जहां आपने डुप्लिकेट किया है stdinया इसकी stdoutएक प्रति सहेजना चाहते हैं और इसे पुनर्स्थापित करना चाहते हैं, जैसे:

मान लें कि आप एक चर में stdout को छोड़ते समय कमांड (और केवल stderr) का स्टैडर पकड़ना चाहते हैं।

कमांड प्रतिस्थापन var=$(cmd), एक पाइप बनाता है। पाइप का राइटिंग एंड cmdस्टडआउट (फाइल डिस्क्रिप्टर 1) बन जाता है और दूसरे सिरे को वेरिएबल को भरने के लिए शेल द्वारा पढ़ा जाता है।

अब, यदि आप stderrचर पर जाना चाहते हैं, तो आप ऐसा कर सकते हैं var=$(cmd 2>&1):। अब दोनों fd 1 (stdout) और 2 (stderr) पाइप पर जाते हैं (और अंत में चर), जो हम चाहते हैं उसका केवल आधा है।

यदि हम करते हैं var=$(cmd 2>&1-)(छोटा है var=$(cmd 2>&1 >&-), तो अब केवल cmdस्टडर पाइप में जाता है, लेकिन fd 1 बंद है। यदि cmdकोई आउटपुट लिखने की कोशिश करता है EBADF, तो वह एक त्रुटि के साथ लौटेगा , यदि वह एक फाइल खोलता है, तो उसे पहली फ्री एफडी मिलेगी और ओपन फाइल को stdoutतब तक सौंपा जाएगा जब तक कि कमांड गार्ड उसके खिलाफ नहीं हो जाता! वह नहीं जो हम चाहते हैं।

यदि हम चाहते हैं कि स्टैडआउट को cmdअकेला छोड़ दिया जाए, तो यह उसी संसाधन की ओर इशारा करता है, जो उसने कमांड प्रतिस्थापन के बाहर की ओर इशारा किया है, तो हमें किसी तरह से उस संसाधन को कमांड प्रतिस्थापन के अंदर लाने की आवश्यकता है। उसके लिए हम कमांड प्रतिस्थापन के stdout बाहर की एक प्रति अंदर ले जा सकते हैं।

{
  var=$(cmd)
} 3>&1

जो लिखने के लिए एक साफ तरीका है:

exec 3>&1
var=$(cmd)
exec 3>&-

(जो अंत में बंद करने के बजाय fd 3 को पुनर्स्थापित करने का लाभ है)।

फिर {(या exec 3>&1) और अप करने के लिए }, दोनों fd 1 और 3 एक ही संसाधन के लिए इंगित करें fd 1 शुरू में बताया। fd 3 कमांड प्रतिस्थापन के अंदर उस संसाधन को भी इंगित करेगा (कमांड प्रतिस्थापन केवल fd 1, stdout को पुनर्निर्देशित करता है)। तो ऊपर, के लिए cmd, हम 1, 2, 3 के लिए मिल गया है:

  1. var के लिए पाइप
  2. अछूता
  3. कमांड प्रतिस्थापन के बाहर 1 अंक के समान

यदि हम इसे बदल देते हैं:

{
  var=$(cmd 2>&1 >&3)
} 3>&1-

फिर यह बन जाता है:

  1. कमांड प्रतिस्थापन के बाहर 1 अंक के समान
  2. var के लिए पाइप
  3. कमांड प्रतिस्थापन के बाहर 1 अंक के समान

अब, हमें वह मिल गया है जो हम चाहते थे: स्टादर पाइप में जाता है और स्टडआउट को अछूता छोड़ दिया जाता है। हालाँकि, हम उस fd 3 को लीक कर रहे हैं cmd

जबकि कमांड (कन्वेंशन द्वारा) एफडीएस को 0 से 2 के लिए खुला माना जाता है और मानक इनपुट, आउटपुट और त्रुटि हो, वे अन्य एफडी के कुछ भी ग्रहण नहीं करते हैं। सबसे अधिक संभावना है कि वे उस fd 3 को अछूता छोड़ देंगे। यदि उन्हें किसी अन्य फ़ाइल डिस्क्रिप्टर की आवश्यकता होती है, तो वे बस वही करेंगे open()/dup()/socket()...जो पहले उपलब्ध फ़ाइल डिस्क्रिप्टर को वापस कर देगा। यदि (एक शेल स्क्रिप्ट की तरह exec 3>&1) जो उन्हें fdविशेष रूप से उपयोग करने की आवश्यकता है , तो वे पहले इसे कुछ को असाइन करेंगे (और उस प्रक्रिया में, हमारे fd 3 द्वारा आयोजित संसाधन को उस प्रक्रिया द्वारा जारी किया जाएगा)।

यह अच्छा है कि fd 3 को बंद करें क्योंकि cmdइसका उपयोग नहीं किया जाता है, लेकिन यदि हम कॉल करने से पहले इसे छोड़ देते हैं तो यह कोई बड़ी बात नहीं है cmd। समस्याएं हो सकती हैं: cmd(और संभावित रूप से अन्य प्रक्रियाएं जो इसे पैदा करती हैं) इसमें एक कम एफडी उपलब्ध है। संभावित रूप से अधिक गंभीर समस्या यह है कि यदि संसाधन कि fd अंक cmdपृष्ठभूमि में उसके द्वारा प्रायोजित प्रक्रिया द्वारा आयोजित किया जा सकता है । यह एक चिंता का विषय हो सकता है यदि वह संसाधन एक पाइप या अन्य अंतर-प्रक्रिया संचार चैनल है (जैसे कि जब आपकी स्क्रिप्ट के रूप में चलाया जा रहा है script_output=$(your-script)), इसका मतलब है कि दूसरे छोर से पढ़ने की प्रक्रिया कभी भी अंत-फ़ाइल को तब तक नहीं दिखाई देगी जब तक कि पृष्ठभूमि की प्रक्रिया समाप्त हो जाती है।

इसलिए यहां लिखना बेहतर है:

{
  var=$(cmd 2>&1 >&3 3>&-)
} 3>&1

जिसे bashछोटा किया जा सकता है:

{
  var=$(cmd 2>&1 >&3-)
} 3>&1

कारणों का योग करने के लिए कि इसका उपयोग शायद ही कभी क्यों किया जाता है:

  1. यह अमानक है और बस वाक्यविन्यास चीनी है। आपको अपनी स्क्रिप्ट को कम पोर्टेबल बनाने के साथ कुछ कीस्ट्रोक्स को बचाने के लिए संतुलन मिला है और लोगों को उस असामान्य सुविधा का उपयोग नहीं करने के लिए कम स्पष्ट है।
  2. यह डुप्लिकेट करने के बाद मूल fd बंद करने के लिए की जरूरत अक्सर अनदेखी की है, क्योंकि समय के सबसे अधिक है, हम परिणाम से है, तो हम बस करो ग्रस्त नहीं है >&3बजाय >&3-या >&3 3>&-

सबूत है कि यह शायद ही कभी इस्तेमाल किया जाता है, जैसा कि आपको पता चला है कि यह बाश में फर्जी है । बैश compound-command 3>&4-या any-builtin 3>&4-पत्तियों में fd 4 बंद होने के बाद भी compound-commandया any-builtinवापस आ गया है। समस्या को ठीक करने के लिए एक पैच अब (2013-02-19) उपलब्ध है।


धन्यवाद, अब मुझे पता है कि एक fd क्या चल रहा है। 2 स्निपेट (और सामान्य रूप से एफडीएस) के बारे में मेरे 4 मूल प्रश्न हैं: 1) cmd1 में, आप 2 (stderr) को 3 की प्रतिलिपि बनाते हैं, अगर कमांड आंतरिक रूप से इस 3 fd का उपयोग करता है तो क्या होगा? 2) 3> और 1 और 4> और 1 काम क्यों करते हैं? 3 और 4 का दोहराव केवल उन दो सेमी में प्रभावी होता है, क्या वर्तमान शेल भी प्रभावित होता है? 3) आप cmd1 में 4 और cmd2 में 3 क्यों बंद करते हैं? उन आदेशों में उल्लेख एफडी का उपयोग नहीं करते हैं, क्या वे करते हैं? 4) आखिरी स्निपेट में, अगर एक गैर-मौजूद व्यक्ति (cmd1 और cmd2 में) को एक fd डुप्लिकेट किया जाता है, तो मेरा क्या मतलब है, क्रमशः 3 और 4?
क्वेंटिन

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

{ var=$(cmd 2>&1 >&3) ; } 3>&1-क्या यह टाइपो 1 को बंद करने में नहीं है?
क्वेंटिन

@ क्वेंटिन, यह एक टाइपो है, जिसमें मुझे नहीं लगता कि मुझे इसे शामिल करने का इरादा है, लेकिन इससे कोई फर्क नहीं पड़ता क्योंकि ब्रेसिज़ के अंदर कुछ भी नहीं है जो fd 1 का उपयोग करता है (यह इसे नकल करने का पूरा बिंदु था fd 3: क्योंकि मूल 1 अन्यथा अंदर पहुंच योग्य नहीं होगा$(...) )।
स्टीफन चेजेलस

1
@ क्वेंटिन में प्रवेश करने पर {...}, fd 3 पॉइंट्स जो fd 1 को इंगित करता है और fd 1 को बंद किया जाता है, फिर प्रवेश करने पर $(...), fd 1 फीड करने वाले पाइप पर सेट होता है $var, फिर cmd2 के लिए और साथ ही 1 से क्या 3 पॉइंट्स तक , कि बाहरी है 1. तथ्य यह है कि 1 बंद रहता है बाद में एक बग में बग है, मैं इसे रिपोर्ट करेंगे। ksh93 जहां से वह फीचर आता है, उस बग में नहीं है।
स्टीफन चेजलस

4

इसका मतलब यह है कि यह उसी जगह को इंगित करता है जहां अन्य फाइल डिस्क्रिप्टर करता है। आप अलग मानक त्रुटि वर्णनकर्ता के स्पष्ट अलग से निपटने से बहुत कम ही ऐसा करने के लिए, की जरूरत है ( stderr, fd 2, /dev/stderr -> /proc/self/fd/2)। यह कुछ जटिल मामलों में काम आ सकता है।

एडवांस बैश स्क्रिप्टिंग गाइड में यह लॉग लेवल उदाहरण और यह स्निपेट है:

# Redirecting only stderr to a pipe.
exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

उदाहरण के लिए स्रोत मैज की टोली में हम एक ही कोड ब्लॉक से अलग-अलग आउटपुट को डिस्कनेक्ट करने के लिए इसका उपयोग करते हैं:

  (
    # everything is set, so run the actual build infrastructure
    run_build
  ) 3> >(tee -a $C_LOG >> /dev/stdout) \
    2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)

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


0

यूनिक्स में, फाइलें फाइल डिस्क्रिप्टर (स्मॉलिश पूर्णांक) द्वारा नियंत्रित की जाती हैं, उदाहरण के लिए मानक इनपुट 0 है, मानक आउटपुट 1 है, मानक त्रुटि 2 है; जैसा कि आप अन्य फाइलों को खोलते हैं जो उन्हें आमतौर पर सबसे छोटा अप्रयुक्त डिस्क्रिप्टर सौंपा जाता है)। इसलिए यदि आप प्रोग्राम के इनड्रेस को जानते हैं, और आप आउटपुट को भेजना चाहते हैं, जो मानक आउटपुट 5 को फाइल डिस्क्रिप्टर में जाता है, तो आप डिस्क्रिप्टर को 5 से 1 पर ले जाएंगे। यही वह जगह है, जहां 2> errorsसे निर्माण होता है, और निर्माण 2>&1में त्रुटियों की नकल करना चाहते हैं। आउटपुट स्ट्रीम।

तो, शायद ही कभी इस्तेमाल किया जाता है (मैं अपने 25+ वर्षों के लगभग अनन्य यूनिक्स उपयोग में गुस्से में एक या दो बार इसका उपयोग करना याद करता हूं), लेकिन जब बिल्कुल आवश्यक हो।


लेकिन फ़ाइल डिस्क्रिप्टर 1 को निम्नलिखित तरीके से डुप्लिकेट करने के लिए क्यों नहीं: 5> और 1? मुझे समझ नहीं आता क्या एफडी चलती के बाद से गिरी के बारे में उसके बाद उसे बंद करने के लिए ... है की फायदा नहीं है
क्वेंटिन

यह 5 को 1 में डुप्लिकेट नहीं करता है, यह 5 को भेजता है जहां 1 जा रहा है। और पूछने से पहले; हां, कई अलग-अलग नोटेशन हैं, जो कई प्रकार के अग्रगामी खोलों से उठाए गए हैं।
वॉनब्रांड

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