sh सिंटैक्स एक वाइल्डकार्ड से मेल खाते हुए शून्य फ़ाइलों को संभालने के लिए और साथ ही अधिक?


0

मैं एक /bin/shशेल स्क्रिप्ट लिखना चाहता हूं जो वाइल्डकार्ड से मेल खाती किसी भी फाइल को संभाल लेगी। 1 या अधिक मिलान वाली फ़ाइलों को संभालना आसान है। हालाँकि, मुझे 0 मिलान फ़ाइलों के मामले को संभालना अजीब लग रहा है।

स्पष्ट निर्माण है:

#!/bin/sh
for f in *.ext; do
  handle "$f"
done

जहाँ *.extएक या एक से अधिक भाव हो सकते हैं, जो शेल फ़ाइल पथों की तुलना करता है, और handleएक शेल कमांड है, जो किसी मौजूदा फ़ाइल को पथ दिए जाने पर सही ढंग से चलता है, लेकिन विफल रहता है यदि वह पथ दे जो फ़ाइल में मैप नहीं करता है। (इस मामले में जो इस सवाल को उकसाता है, वे हैं *.flacऔर ffmpeg, लेकिन मुझे लगता है कि कोई फर्क नहीं पड़ता।)

अगर वहाँ फ़ाइलों मिलान कर रहे हैं foo.ext, bar.extहै, तो इस स्क्रिप्ट करता है

handle "foo.ext"
handle "bar.ext"

जैसा सोचा था। हालाँकि, यदि कोई मेल नहीं होती है, तो यह स्क्रिप्ट एक त्रुटि संदेश देती है, जैसे

handle: *.ext: No such file or directory

मुझे लगता है कि मैं समझता हूं कि ऐसा क्यों होता है: बैश मैन पेज (जो मुझे लगता है कि मान्य /bin/shभी है) कहते हैं, "निम्नलिखित शब्दों की सूची का inविस्तार किया गया है, वस्तुओं की एक सूची तैयार की जा रही है। ... यदि परिणामों का अनुसरण खाली में किया जाता है। सूची, कोई आदेश निष्पादित नहीं किए जाते हैं, और वापसी की स्थिति 0. है। " जाहिर है जब *.ext"विस्तारित" होता है, तो परिणामों की सूची में *.extखाली सूची के बजाय शामिल है । लेकिन यह नहीं समझाता कि इसे कैसे रोकें।

(अपडेट: sh (1)हिरलूम प्रोजेक्ट का एक मैन पेज है जो बॉर्न शेल का वर्णन करता है sh, न कि बाद में फिर से बॉर्न शेल का bashफ़ाइल नाम जनरेशन के तहत , यह स्पष्ट रूप से कहता है, "यदि कोई फ़ाइल नाम नहीं मिला है जो पैटर्न से मेल खाता है तो शब्द बचा है। अपरिवर्तित। "यह बताता है कि सूची में shएक *.extपैटर्न क्यों छोड़ता है ।"

इस लूप को लिखने के लिए एक कॉम्पैक्ट, मुहावरेदार तरीका क्या है ताकि यह कोई मेल नहीं होने वाली फ़ाइलों के साथ शून्य बार चले, लेकिन प्रत्येक मिलान फ़ाइल के लिए एक बार चलेगा? बेहतर अभी तक, क्या कई पैटर्न के साथ काम करेंगे जैसे:

for f in *.ext1 special.ext1 *.ext2; do ...

/bin/shपोर्टेबिलिटी के लिए मैं सिंटैक्स के साथ संगत का उपयोग करना पसंद करता हूं । मैं मैक ओएस एक्स 10.11.6 का उपयोग कर रहा हूं, लेकिन मैं सिंटैक्स की उम्मीद कर रहा हूं जो किसी भी यूनिक्स जैसे ओएस पर काम करता है।

मैं इस तरह के एक लूप लिखने के लिए अनाड़ी, गैर-मुहावरेदार तरीकों के साथ आया हूं। अगर मुझे तुरंत अच्छे उत्तर नहीं मिलते हैं, तो मैं रिकॉर्ड के लिए, उत्तर के रूप में योगदान करूँगा।


यदि आप बैश तक जाने के इच्छुक हैं तो यह संभालना तुच्छ है।
इग्नासियो वाज़क्वेज़-अब्राम्स

@ इग्नासियोवेज़केज़-एबर्स मैं छड़ी करना पसंद करता हूं /bin/sh, लेकिन यह पूर्ण नहीं है। मुझे लगता है कि फॉर्म का जवाब "बैश पर स्विच करें, फिर ऐसा करें" सहायक है। यह उतना अच्छा नहीं होगा जितना कि यह कहा जाता है, "यहाँ यह श में कैसे करना है", लेकिन शायद ऐसा जवाब मौजूद नहीं है।
जिम डेलाउंट

बैश है nullglob, जो लूप को चलने से भी रोकेगा।
इग्नासियो वाज़क्वेज़-अब्राम्स

महान! उसे एक अच्छे उत्तर में बदल दें, और आपको अंक मिलेंगे।
जिम डिलांट

जवाबों:


2

ऐसा करने के लिए सबसे सरल पोर्टेबल तरीका है लूप को छोड़ना, यदि विस्तार कुछ उत्पन्न नहीं करता है जो वास्तव में मौजूद है:

for f in *.ext1 *.ext2; do
  [ -e "$f" ] || continue
  handle "$f"
done

स्पष्टीकरण: मान लें कि कई .ext2 फ़ाइलें हैं, लेकिन कोई .ext1 फ़ाइलें नहीं हैं। उस स्थिति में वाइल्डकार्ड का विस्तार होगा *.ext1 filea.ext2 fileb.ext2 filec.ext2। तो [ -e "*.ext1" ]विफल रहता है और यह चलता है continue, जो लूप के उस पुनरावृत्ति को छोड़ देता है। फिर [ -e "filea.ext2" ]आदि सफल होते हैं, और लूप के वे पुनरावृत्तियों सामान्य रूप से चलते हैं।

BTW, आप इसे [ -f "$f" ] || continueएक सादा फ़ाइल नहीं है (यदि आप निर्देशिकाओं को छोड़ना चाहते हैं) को छोड़ कर उदा ।

बेशक, अगर आप बैश के नीचे चल रहे हैं, तो आप बस shopt -s nullglobपहले चला सकते हैं ताकि यह सूची में बेजोड़ पैटर्न न छोड़े। और फिर shopt -u nullglobबाद में अनपेक्षित दुष्प्रभावों को रोकने के लिए (उदाहरण के grep pattern *.nonexistentलिए यदि स्टड से पढ़ने की कोशिश की जाएगी यदि nullglobसेट किया गया है)।


continueऔर उत्कृष्ट स्पष्टीकरण का चतुर उपयोग । मुझे लगता है कि nullglobअगर मैं इसे सही तरीके से समझूं तो दूसरे उत्तर में बदल जाता है। @Ignacio Vazquez-Abrahams ने सवाल के तहत एक टिप्पणी में इसका उल्लेख किया।
जिम डेलाउंट

0

बॉर्न शेल ( /bin/bash) से बॉर्न अगेन शेल ( ) पर स्विच करें /bin/sh, और एक सरल समाधान संभव हो जाता है।

bash(1)आदमी पेज का उल्लेख है nullglob विकल्प:

यदि सेट किया गया है, तो बैश उन पैटर्नों की अनुमति देता है, जो अपने आप की बजाए नल स्ट्रिंग में विस्तार करने के लिए बिना फाइल्स के मेल खाते हैं ( ऊपर पाथनाम विस्तार देखें )।

पथ विस्तार खंड का कहना है:

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

तो, नलग्लोब विकल्प सेट करना पैटर्न के लिए वांछित व्यवहार देता है। यदि कोई फ़ाइल पैटर्न से मेल नहीं खाती है, तो लूप निष्पादित नहीं करता है।

#!/bin/bash
shopt -s nullglob
for f in *.ext; do
  handle "$f"
done

लेकिन नलग्लोब विकल्प अच्छी तरह से अन्य आदेशों के लिए वांछित व्यवहार में हस्तक्षेप कर सकता है। तो, आपको संभवतः नलग्लोब की मौजूदा सेटिंग को बचाने के लिए , और बाद में इसे पुनर्स्थापित करने में समझदारी मिलेगी । सौभाग्य से, shopt -pनिर्मित कमांड एक आउटपुट का उत्पादन करता है जिसे इनपुट के रूप में पुन: उपयोग किया जा सकता है। लेकिन यह सभी विकल्पों के लिए आउटपुट का उत्सर्जन करता है, इसलिए nullobj सेटिंग grepको चुनने के लिए उपयोग करें । नीचे लॉग देखें (जहां bash कमांड प्रॉम्प्ट इंगित करता है):$

$ shopt -p | grep nullglob
shopt -u nullglob
$ shopt -s nullglob
$ shopt -p | grep nullglob
shopt -s nullglob

तो, वाइल्डकार्ड पैटर्न से मेल खाते शून्य या अधिक फ़ाइल को संभालने के लिए अंतिम बैश सिंटैक्स इस तरह दिखता है:

#!/bin/bash
SAVED_NULLGLOB=$(shopt -p | grep nullglob)
shopt -s nullglob
for f in *.ext; do
  handle "$f"
done
eval "$SAVED_NULLGLOB"

साथ ही, प्रश्न में कई पैटर्न का उल्लेख किया गया है, जैसे:

for f in *.ext1 special.ext1 *.ext2; do ...

Nullglob विकल्प सूची है जो एक "पैटर्न" नहीं है में एक शब्द को प्रभावित नहीं करता है, तो special.ext1अभी भी पाश के लिए पारित कर दिया जाएगा। इसके लिए एकमात्र समाधान है @ गॉर्डन डेविसन की continueअभिव्यक्ति [ -e "$f" ] || continue

पावती: दोनों @Ignacio वेज़क्वेज़-अब्राम और @Gordon Davisson का संकेत bash(1)है और इसके nullglob विकल्प। धन्यवाद। चूंकि उन्होंने तुरंत एक पूरी तरह से काम करने वाले उदाहरण के साथ जवाब देने में योगदान नहीं दिया, इसलिए मैं खुद ऐसा कर रहा हूं।

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