इसे सोर्स करने से पहले फाइल के अस्तित्व की जाँच क्यों करें?


13

जब किसी फ़ाइल को स्रोत करने का प्रयास किया जाता है, तो क्या आप यह कहना नहीं चाहेंगे कि फ़ाइल मौजूद नहीं है इसलिए आपको पता है कि क्या ठीक करना है?

उदाहरण के लिए, nvm आपकी प्रोफ़ाइल / rc में इसे जोड़ने की अनुशंसा करता है:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

ऊपर के साथ, यदि nvm.shमौजूद नहीं है, तो आपको "मूक त्रुटि" मिलेगी। लेकिन अगर आप कोशिश . "$NVM_DIR/nvm.sh"करेंगे तो आउटपुट होगा FILE_PATH: No such file or directory


3
आपको नहीं करना चाहिए यह रस्म है। स्रोत का प्रयास करें फिर त्रुटि को हैंडल करें, यदि कोई हो
मिकेल

2
@ मिकेल सही! फिर मैं इसे हर जगह क्यों देखता हूं?
जेबलिन

3
@FaheemMitha, ठीक है, यदि आप जो कर रहे हैं वह सभी सुरक्षा-संवेदनशील (यानी आपका कार्यक्रम किसी और की ओर से काम करता है) है, तो आपको वास्तव में दौड़ की स्थिति ( TOCTOU ) के बारे में परवाह करने की आवश्यकता है । शायद यहाँ ऐसा नहीं है, हालाँकि, अगर आपको बड़ी समस्याओं का सामना करना पड़ता है अगर किसी को फ़ाइलों को संशोधित करना पड़ता है HOME
इलकाचू

8
@FaheemMitha पहले अस्तित्व के लिए जाँच करना कभी बेहतर नहीं है। परीक्षण और उपयोग के बीच स्थिति बदल सकती है, झूठी सकारात्मक और झूठी नकारात्मक दोनों उपज: और आपको अभी भी वैसे भी उपयोग पर विफलता को संभालना है। और जैसा कि आपने प्रदर्शन का उल्लेख किया है, उपयोग करने से पहले परीक्षण, दो बार के रूप में धीमा है ऐसा नहीं कर रहा है और सिस्टम को ऐसा करने दे रहा है, जो यह वैसे भी करने जा रहा है। यह नहीं हो सकता।
user207421

1
@SimonRichter, इस मामले में nvm काम नहीं करेगा। उपयोगकर्ता को यह पता लगाना होगा कि फ़ाइल अपने आप से गायब है।
JBallin

जवाबों:


25

POSIX गोले में, .एक विशेष बिलिन है, इसलिए इसकी विफलता से शेल बाहर निकल जाता है (जैसे कुछ गोले में bash, यह केवल तभी किया जाता है जब POSIX मोड में हो)।

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

यहाँ कर रहे हैं:

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

एक ऐसा मामला है जहां आप फ़ाइल को स्रोत करना चाहते हैं यदि यह वहां है, और नहीं तो यह नहीं है (या यहां खाली नहीं है -s)।

यही है, इसे एक त्रुटि नहीं माना जाना चाहिए (POSIX गोले में घातक त्रुटि) यदि फ़ाइल नहीं है, तो उस फ़ाइल को एक वैकल्पिक फ़ाइल माना जाता है।

यदि फ़ाइल पठनीय नहीं थी या निर्देशिका (या कुछ गोले में) थी तो यह अभी भी एक (घातक) त्रुटि होगी यदि इसे पार्स करते समय एक सिंटैक्स त्रुटि हुई थी जो वास्तविक त्रुटि स्थिति होगी जिसे रिपोर्ट किया जाना चाहिए।

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

दूसरी ओर,

command . "$NVM_DIR/nvm.sh" 2> /dev/null

जहां command कमांड के लिए विशेष विशेषता को commandहटाता है (इसलिए यह त्रुटि पर शेल से बाहर नहीं निकलता है) निम्नानुसार काम नहीं करेगा:.

  • यह .त्रुटियों को छिपाएगा, लेकिन आदेशों की त्रुटियां खट्टी फाइल में भी चलेंगी
  • यह गलत अनुमतियों वाली फ़ाइल की तरह वास्तविक त्रुटि शर्तों को भी छिपाएगा।

अन्य सामान्य वाक्यविन्यास (इनिट grep -r /etc/default /etc/init*स्क्रिप्ट के लिए डेबियन सिस्टम पर उदाहरण के लिए देखें जो systemdअभी तक परिवर्तित नहीं हुए हैं (जहां EnvironmentFile=-/etc/default/serviceइसके बजाय वैकल्पिक पर्यावरण फ़ाइल निर्दिष्ट करने के लिए उपयोग किया जाता है) में शामिल हैं:

  • [ -e "$file" ] && . "$file"

    यदि यह खाली है तो फ़ाइल को चेक करें, फिर भी इसे सोर्स करें। यदि यह खोला जा सकता है (तब भी, या वहाँ था) फिर भी घातक त्रुटि। आप अधिक प्रकार देख सकते हैं [ -f "$file" ]( जैसे मौजूद है और एक नियमित फ़ाइल है), [ -r "$file" ](पठनीय है), या उन का संयोजन।

  • [ ! -e "$file" ] || . "$file"

    थोड़ा बेहतर संस्करण। यह स्पष्ट करता है कि मौजूद फाइल ठीक नहीं है। इसका मतलब यह भी है कि $?पिछले आदेश में $file(पिछले मामले में, यदि आप प्राप्त करते हैं 1, तो आप यह नहीं जानते हैं कि क्या यह $fileमौजूद नहीं था या यदि वह आदेश विफल हो गया है)

  • command . "$file"

    फ़ाइल होने की अपेक्षा करें, लेकिन अगर यह व्याख्या नहीं की जा सकती है तो बाहर न निकलें।

  • [ ! -e "$file" ] || command . "$file"

    उपरोक्त संयोजन: यह ठीक है यदि फ़ाइल नहीं है, और पॉसिक्स गोले के लिए, फ़ाइल खोलने (या पार्स) के लिए विफलताएं रिपोर्ट की गई हैं, लेकिन घातक नहीं हैं (जो अधिक वांछनीय हो सकता है ~/.profile)।


However नोट: zshहालाँकि, आप commandउस तरह का उपयोग नहीं कर सकते जब तक कि shअनुकरण में; ध्यान दें कि कोर्न शेल में, sourceवास्तव में command ., एक गैर-विशेष संस्करण है.


दिलचस्प! मुझे नहीं पता था कि पोसिक्स श के बारे में। लेकिन सवाल यह था .bash_profile। मुझे लगता है कि सॉरी की तुलना में बेहतर सुरक्षित है, लेकिन क्या पीओएसआईएक्स मोड में कभी खट्टा होने पर बैश किया .bash_profileजाता है?
मिकेल

(मुझे लगता है कि आप इस प्रश्न की व्याख्या कर सकते हैं क्योंकि प्रश्न में एनवीएम स्रोत पढ़ने के आधार पर सभी पोसिक्स गोले पर अधिक व्यापक रूप से लागू किया जा सकता है।)
मिकेल

@ मायिक, मेरा जवाब तब भी लागू होता है bashजब पोसिक्स मोड में नहीं होता है। [ -e /file ] && . /fileयदि आप इसे फ़ाइल नहीं होने पर त्रुटि मानते हैं, तो आप चाहेंगे । कोशिश स्रोत तो, त्रुटि संभाल यदि कोई यहाँ नहीं किया जा सकता।
स्टीफन चेज़लस

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

1
दौड़ की स्थिति के संबंध में - यह लॉगिन के लिए एक बार विफल होने के लिए IMHO की समस्या से बहुत कम है (जबकि सिस्टम पर कुछ अजीब हो रहा है) लॉगिन को लगातार विफल करने की तुलना में (भले ही उपयोगकर्ता के कॉन्फ़िगरेशन में कुछ नहीं-बिल्कुल सही हो -files)। तो इस चेक में एक दौड़ की स्थिति अभी भी चेक नहीं होने पर एक सुधार है।
at18

5

अनुरक्षक की nvmप्रतिक्रिया:

बस फ़ाइल को हटाकर एनवीएम को अनइंस्टॉल करना आसान है; अतिरिक्त काम करने के लिए (नीचे ट्रैक करने के लिए जहां लाइन है कि स्रोत एनवीएम हैं) विशेष रूप से मूल्यवान नहीं लगते हैं।

मेरी व्याख्या (स्टीफन की उत्कृष्ट व्याख्या और कुसलानंद की टिप्पणी के साथ संयुक्त):

यह सरल और सुरक्षित है।

यह एक लापता फ़ाइल (विभिन्न कारणों से) के कारण स्टार्टअप पर निकलने वाले POSIX गोले से बचाव करता है। यदि वे पसंद करते हैं तो गैर-पॉसिक्स (जैसे बैश) के गोले का उपयोग सशर्त निकाल सकते हैं।


1
मैं आपकी व्याख्या का विरोध करता हूं कि यह "शुरुआती के उद्देश्य से" है। यह रक्षात्मक है। आप उपयोगकर्ता के लॉगिन शेल को स्टार्टअप पर अनपेक्षित रूप से सिर्फ इसलिए समाप्त नहीं करना चाहते हैं क्योंकि वह फ़ाइल गायब हो गई है ( जो भी कारण हो)। यदि कोड की वह पंक्ति शेल इनिट फ़ाइलों में से एक में है /etc, तो यह कुछ उपयोगकर्ताओं के लिए फ़ाइल की अनुमति देता है, और कुछ में फ़ाइल नहीं है। IMHO, nvmअनुरक्षक की प्रतिक्रिया केवल एक पहलू पर छू रही है।
Kusalananda

1

जैसा कि जेब्लिन और स्टीफन चेज़लस ने बताया है, पोसिक्स के गोले में, एक फाइल को सोर्स करना जो मौजूद नहीं है, लॉगिंग को विफल करने का कारण होगा।

लेकिन यह देखने के लिए एक परीक्षण जोड़ना कि क्या फ़ाइल मौजूद है और फिर स्रोत की कोशिश करने से यह कुछ हो सकता है जिसे रेस की स्थिति कहा जाता है। यदि और के nvm.shबीच में कुछ परिवर्तन होता है , तो यह वास्तव में बग को रोकने की कोशिश करेगा, भले ही शायद ही कभी।[ -s nvm.sh ]. nvm.sh

सामान्य तौर पर, दौड़ की स्थिति को रोकने का तरीका यह है कि जिस चीज़ को आप करना चाहते हैं, बस उसे आज़माएं, फिर असफल होने पर उसे संभालें, जैसे

. "$NVM_DIR/nvm.sh" || echo "Sourcing $NVM_DIR/nvm.sh failed" >&2

यह पता चला है कि यह POSIX गोले में काम नहीं करता है, क्योंकि, ऊपर के रूप में, .विफलता किसी भी त्रुटि से निपटने से पहले शेल को तुरंत बाहर निकलने का कारण बन सकती है।

मेरा जवाब तर्क है कि POSIX गोले इस प्रश्न के लिए प्रासंगिक नहीं हैं, क्योंकि .bash_profileकभी POSIX मोड में नहीं चलना चाहिए। तो हम ऊपर दिए गए कोड को वैसे भी कर सकते हैं।

सबसे सुरक्षित होने के लिए, हम यह सुनिश्चित कर सकते हैं कि POSIX मोड प्रभावी नहीं है, या यह सुनिश्चित करें कि POSIX मोड /unix//a/383581/3169 में वर्णित तकनीक का उपयोग करके अक्षम है ।

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

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