कोड में "{निष्पादन> / देव / अशक्त; }> / देव / अशक्त "हुड के तहत क्या हो रहा है?


15

जब आप एक आदेश सूची को पुनर्निर्देशित करते हैं, जिसमें निष्पादन> / dev / null लगता है, तब भी लागू नहीं होता है, जैसे कि:

{ exec >/dev/null; } >/dev/null; echo "Hi"

"हाय" छपा है।

मैं इस धारणा के तहत था कि {}जब तक यह एक पाइपलाइन का हिस्सा नहीं है, कमांड लिस्ट को एक उपधारा नहीं माना जाता है, इसलिए मुझे exec >/dev/nullअभी भी मेरे दिमाग में वर्तमान शेल पर्यावरण के भीतर लागू किया जाना चाहिए।

अब अगर आप इसे बदलेंगे:

{ exec >/dev/null; } 2>/dev/null; echo "Hi"

अपेक्षित रूप से कोई आउटपुट नहीं है; फ़ाइल डिस्क्रिप्टर 1 में भविष्य के कमांड के लिए / dev / null के रूप में अच्छी तरह से बताया गया है। यह फिर से दिखाया गया है:

{ exec >/dev/null; } >/dev/null; echo "Hi"

जो कोई आउटपुट नहीं देगा।

मैंने एक स्क्रिप्ट बनाने और इसे स्ट्रगल करने की कोशिश की, लेकिन मैं अभी भी अनिश्चित हूं कि यहां क्या हो रहा है।

इस स्क्रिप्ट के प्रत्येक बिंदु पर STDOUT फाइल डिस्क्रिप्टर का क्या हो रहा है?

संपादित करें: मेरा स्ट्रेस आउटपुट जोड़ना:

read(255, "#!/usr/bin/env bash\n{ exec 1>/de"..., 65) = 65
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
close(10)                               = 0
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0
fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, TCGETS, 0x7ffee027ef90)        = -1 ENOTTY (Inappropriate ioctl for device)
write(1, "hi\n", 3)                     = 3

वह अजीब है; मैं पुन: पेश नहीं कर सकता close(10)। क्या आप अपनी संपूर्ण स्क्रिप्ट सामग्री भी पोस्ट कर सकते हैं, जिस पर आपने स्ट्रेस चलाया था?
डिप्रेस्डैनियल

: @DepressedDaniel यहाँ पूर्ण स्क्रिप्ट और strace है स्क्रिप्ट strace
जॉय Pabalinas

आप एक आवारा है ;के बाद }जो के अर्थ बदल जाता है, > /dev/nullके लिए यौगिक सूची पर लागू नहीं {}सब के बाद।
डिप्रैडडैनियल

@DepressedDaniel आह, आप पूरी तरह से सही हैं! अब आउटपुट वही है जिसकी मुझे उम्मीद है; आपके जवाबों के लिये धन्यवाद!
जॉय पाबलिनस

जवाबों:


17

चलो पीछा करते है

{ exec >/dev/null; } >/dev/null; echo "Hi"

क्रमशः।

  1. दो आदेश हैं:

    ए। { exec >/dev/null; } >/dev/null, के बाद

    ख। echo "Hi"

    शेल पहले कमांड (ए) और फिर कमांड (बी) निष्पादित करता है।

  2. { exec >/dev/null; } >/dev/nullआय का निष्पादन निम्नानुसार है:

    ए। सबसे पहले, शेल पुनर्निर्देशन करता है >/dev/null और कमांड के समाप्त होने पर इसे पूर्ववत करना याद रखता है

    ख। फिर, शेल निष्पादित होता है { exec >/dev/null; }

    सी। अंत में, शेल मानक आउटपुट को वापस स्विच करता है जहां था। (यह उसी तरह की व्यवस्था है जैसे ls -lR /usr/share/fonts >~/FontList.txt- पुनर्निर्देशन केवल उस कमांड की अवधि के लिए किया जाता है जिसमें वे संबंधित हैं।)

  3. एक बार पहला कमांड किया जाता है शेल निष्पादित होता है echo "Hi"। मानक आउटपुट वह है जहां वह पहले कमांड से पहले था।


क्या 2 बी से पहले 2 ए को निष्पादित करने के पीछे कोई कारण है? (दाएं-से-बाएं)
जॉय पाब्लिनस

5
पुनर्निर्देशन को उस आदेश से पहले निष्पादित किया जाना चाहिए जिसमें वे लागू होते हैं, नहीं? वे अन्यथा कैसे काम कर सकते थे?
एलेक्सपी

अहा, ऐसा कभी नहीं सोचा था! पहले दो बोर महान जवाब हैं; एक पर फैसला करने से पहले इसे थोड़ा सा देना, लेकिन मैं दोनों स्पष्टीकरणों की सराहना करता हूं!
जॉय पाबलिनास

दुर्भाग्य से मैं केवल एक ही उत्तर चुन सकता हूं, इसलिए मैं इसके साथ जा रहा हूं क्योंकि यह थोड़ा कम तकनीकी है और इस प्रकार मुझे लगता है कि यह कम तकनीक वाले उपयोगकर्ताओं की भी मदद करने में सक्षम होगा। हालाँकि @DepressedDaniel का यहाँ भी उतना ही शानदार जवाब था जो गहराई से स्पष्टीकरण देता है।
जॉय पाबलिनास

14

एक उप-शेल या उप-प्रक्रिया का उपयोग नहीं करने के लिए, जब एक यौगिक सूची {}का आउटपुट पाइप किया जाता है >, तो शेल यौगिक सूची को चलाने से पहले STDOUT विवरणक को सहेजता है और इसके बाद इसे पुनर्स्थापित करता है। इस प्रकार exec >यौगिक सूची में अपना प्रभाव उस बिंदु पर नहीं ले जाता है जहाँ पुराने विवरणक को STDOUT के रूप में पुनः स्थापित किया गया है।

आइए, इसके प्रासंगिक भाग पर एक नज़र डालें strace bash -c '{ exec >/dev/null; } >/dev/null; echo hi' 2>&1 | cat -n:

   132  open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
   133  fcntl(1, F_GETFD)                       = 0
   134  fcntl(1, F_DUPFD, 10)                   = 10
   135  fcntl(1, F_GETFD)                       = 0
   136  fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
   137  dup2(3, 1)                              = 1
   138  close(3)                                = 0
   139  open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
   140  fcntl(1, F_GETFD)                       = 0
   141  fcntl(1, F_DUPFD, 10)                   = 11
   142  fcntl(1, F_GETFD)                       = 0
   143  fcntl(11, F_SETFD, FD_CLOEXEC)          = 0
   144  dup2(3, 1)                              = 1
   145  close(3)                                = 0
   146  close(11)                               = 0
   147  dup2(10, 1)                             = 1
   148  fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
   149  close(10)                               = 0

आप देख सकते हैं कि कैसे, लाइन 134 पर, डिस्क्रिप्टर 1( STDOUT) को कम से कम इंडेक्स के साथ दूसरे डिस्क्रिप्टर पर कॉपी किया जाता है 10(यह वही F_DUPFDकरता है; यह उस डिस्क्रिप्टर पर डुप्लिकेट करने के बाद दिए गए नंबर पर शुरू होने वाला सबसे कम उपलब्ध डिस्क्रिप्टर लौटाता है)। यह भी देखें कि कैसे, लाइन 137 पर, open("/dev/null")(डिस्क्रिप्टर 3) के परिणाम को डिस्क्रिप्टर 1( STDOUT) पर कॉपी किया जाता है । अंत में, लाइन पर 147, STDOUTडिस्क्रिप्टर पर सहेजे गए पुराने को 10वापस डिस्क्रिप्टर 1( STDOUT) पर कॉपी किया जाता है । शुद्ध प्रभाव STDOUTलाइन पर परिवर्तन 144(जो आंतरिक से मेल खाती है exec >/dev/null) को इन्सुलेट करना है ।


चूंकि एफडी 1 एफडी 3 द्वारा लाइन 137 पर ओवरराइट किया जाता है, इसलिए लाइन 141 प्वाइंट 10 / देव / नल को इंगित नहीं करता है?
जॉय पाबलिनास

@JoeyPabalinas रेखा 141 10 के बाद अगले उपलब्ध डिस्क्रिप्टर को FD 1 (यानी, stdout) की नक़ल कर रही है , जो 11 वर्ष की हो जाती है, जैसा कि आप उस सिस्टम कॉल से रिटर्न वैल्यू में देख सकते हैं। 10 को बस बैश में हार्ड-कोड किया गया है ताकि bash के डिस्क्रिप्टर सेविंग आपके द्वारा स्क्रिप्ट में हेरफेर करने वाले एकल-अंक डिस्क्रिप्टर के साथ हस्तक्षेप न करें exec
डिप्रेस्डैनियल

तो फिर fcntl (1, F_DUPFD, 10) हमेशा STDOUT का उल्लेख करेगा चाहे एफडी 1 वर्तमान में इंगित कर रहा हो?
जॉय पाबलिनस

@JoeyPabalinas यकीन नहीं होता कि आपका सवाल क्या है। FD 1 IS STDOUT है। वे एक जैसी ही चीज हैं।
डिप्रेस्डैनियल

मेरे मूल पोस्ट में पूर्ण स्ट्रेस आउटपुट जोड़ा गया।
जॉय पाबलिनास

8

के बीच का अंतर { exec >/dev/null; } >/dev/null; echo "Hi"और { exec >/dev/null; }; echo "Hi"डबल पुनर्निर्देशन करता है वह यह है कि dup2(10, 1);इससे पहले कि समापन fd 10 जो मूल की प्रतिलिपि है stdout, अगले आदेश चलाने से पहले ( echo)।

यह उस तरह से होता है क्योंकि बाहरी रीडायरेक्ट वास्तव में आंतरिक रीडायरेक्ट को ओवरले कर रहा है। इसीलिए stdoutयह पूरा होते ही मूल fd को वापस कॉपी कर लेता है।


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