बैश में पथ नामों पर पैटर्न मिलान


10

मैं एक निर्देशिका में उपनिर्देशिकाओं की एक सूची पर कार्य करना चाहता हूं। विचार करें:

for x in x86-headers/*/C/populate.sh; do echo $x; done

यह देता है

x86-headers/elf/C/populate.sh
x86-headers/gl/C/populate.sh
x86-headers/gmp/C/populate.sh
x86-headers/gnome2/C/populate.sh
x86-headers/gtk2/C/populate.sh
x86-headers/jni/C/populate.sh
x86-headers/libc/C/populate.sh

लेकिन मैं मान पथ के दूसरे भाग से मेल खाती है, कि चाहते हैं elf, glआदि मैं जानता हूँ कि बंद पट्टी करने के लिए कैसे अग्रणी x86-headers

for x in x86-headers/*/C/populate.sh; do i=${x##x86-headers/}; echo $i; done

जो देता है

elf/C/populate.sh
gl/C/populate.sh
gmp/C/populate.sh
gnome2/C/populate.sh
gtk2/C/populate.sh
jni/C/populate.sh
libc/C/populate.sh

मुझे यह भी पता है कि रास्ते में बाद के शब्दों को कैसे अलग करना है। यानी एक स्तर नीचे जा रहा है:

cd x86-headers
for x in */C/populate.sh; do i=${x%%/*}; echo $i; done

देता है

elf
gl
gmp
gnome2
gtk2
jni
libc

लेकिन, इनको मिलाने की कोशिश नहीं की जाती है। अर्थात

for x in x86-headers/*/C/populate.sh; do i=${${x##x86-headers}%%/*}; echo $i; done

देता है

bash: ${${x##x86-headers}%%/*}: bad substitution

कोई शक नहीं कि यह गलत सिंटैक्स है, लेकिन मुझे सही सिंटैक्स नहीं पता है। बेशक ऐसा करने के लिए बेहतर तरीके हो सकते हैं। अगर मैं पायथन का उपयोग कर रहा था, तो मैं /एक सूची में प्रत्येक पथ को तोड़ने के लिए विभाजन का उपयोग करूंगा , और फिर दूसरा तत्व चुनूंगा, लेकिन मुझे नहीं पता कि कैसे इसे bash में किया जाए।

संपादित करें: उत्तर के लिए धन्यवाद। मुझे यह भी पूछना चाहिए था कि क्या ऐसा करना संभव है, और यदि ऐसा है तो कैसे?

जवाबों:


9

आप उन्हें bash(या POSIXly) के साथ जोड़ नहीं सकते , आपको इसे दो चरणों में करना होगा।

i=${x#*/}; i=${i%%/*}

यह किसी भी POSIX शेल के लिए पोर्टेबल है। यदि आपको बॉर्न शेल के लिए पोर्टेबिलिटी की आवश्यकता है (लेकिन आप अपने प्रश्न / बैश को टैग क्यों करेंगे ?) साथ ही साथ आप सोलारिस में पोर्ट कर रहे हैं और वहां /bin/shमानक के बजाय उपयोग करने के लिए मजबूर हैं sh, या 20 साल पुराने सिस्टम में पोर्ट कर रहे हैं) , आप इस दृष्टिकोण का उपयोग कर सकते हैं (जो कि POSIX गोले के साथ भी काम करेगा):

IFS=/; set -f
set x $x
i=$3

(ऊपर बहुत ही दुर्लभ मामलों में से एक है जहाँ यह एक चर को छोड़ने के लिए समझ में आता है)।

बस रिकॉर्ड के लिए, zsh के साथ:

print -rl -- x86-headers/*/C/populate.sh(:h:h:t)

( टी के बीमार के EAD फ़ाइल के EAD)।

या आपके pythonदृष्टिकोण के लिए:

x=elf/C/populate.sh
i=${${(s:/:)x}[2]}

अर्धविराम i=${x#*/}; i=${i%%/*}वैकल्पिक है, है ना?
दिमित्रे रादोलोव

3
@DimitreRadoulov यह वैकल्पिक है लेकिन कुछ पुराने संस्करणोंashdash जैसे / के कुछ पुराने संस्करणों को दाएं से बाएं (जो कि कैसे बॉर्न शेल व्यवहार किया गया था) चलाएगा और इस मामले में समस्या पैदा करेगा।
स्टीफन चेज़लस

7

पैरामीटर विस्तार आपको घोंसला बनाने की अनुमति नहीं देता है, इसलिए आपको असाइनमेंट के साथ दो चरणों में करने के लिए मजबूर किया जाता है:

i=${x##x86-headers/}
i=${i%%/*}

आपके पास यहां सरणियों का उपयोग करने का विकल्प है, लेकिन मुझे लगता है कि यह उपरोक्त पीई की तुलना में अधिक बोझिल होगा:

IFS=/
set -f # Disable globbing in case unquoted $x has globs in it
i=( $x )
set +f
echo "${i[1]}"

फिर बाहरी कमांड का उपयोग करने का तीसरा विकल्प है। फ़ाइलों की एक छोटी सूची के साथ यह आमतौर पर सबसे कम कुशल विकल्प है, लेकिन यह सबसे अच्छा होगा क्योंकि फाइलों की संख्या बढ़ती है:

# For clarity, assume no names have linebreaks in them
find . -name populate.sh | cut -d / -f 3

यह सभी गोले का सच नहीं है, हालांकि zshघोंसले के शिकार की अनुमति देता है।
स्टीफन चेज़लस

3
@StephaneChazelas काफी उचित है, लेकिन यह सवाल टैग है bash
कोजिरो

2
regex="x86-headers/([^/]*)/.*"
for f in x86-headers/*/C/populate.sh; do [[ $f =~ $regex ]] && echo "${BASH_REMATCH[1]}"; done
elf
gl
gmp
gnome2
gtk2
jni
libc

2

बलो के रूप में एक निर्माण का उपयोग करके बहुत सावधान रहें:

# What happen if no files match ?
for x in x86-headers/*/C/populate.sh; do
  x=${x##x86-headers/}
  x=${x%%/*}
  echo $x
done

जैसा कि आप अंत में एक कर सकता है echo *। इस मामले में, यह केवल हास्यास्पद है, लेकिन यह बहुत खराब हो सकता है अगर आपके जड़, अपने कर रहे हैं CWDहै /और आप में से कुछ उप निर्देशिकाओं को हटाना चाहते हैं /tmpउन्हें गूंज के बजाय!

इसे ठीक करने के लिए, आप कर सकते हैं:

shopt -s nullglob
for x in x86-headers/*/C/populate.sh; do
  x=${x##x86-headers/}
  x=${x%%/*}
  echo $x
done
shopt -u nullglob

shopt -u nullglobलूप के बाद डिफ़ॉल्ट बैश व्यवहार को पुनर्स्थापित करने के लिए अंत में ध्यान दें ।

अधिक पोर्टेबल समाधान हो सकता है:

for x in x86-headers/*/C/populate.sh; do
  [ -e $x ] || break
  x=${x##x86-headers/}
  x=${x%%/*}
  echo $x
done

( ##और %%पैरामीटर पदार्थ ksh88 में मान्य हैं )।

Nullglob की अधिक संपूर्ण चर्चा के लिए इस लिंक का अनुसरण करें ।

ध्यान दें कि यदि आपके नाम में एक स्थान के साथ एक मध्यस्थ निर्देशिका है, तो उपरोक्त 3 समाधान विफल हो जाते हैं। इसे हल करने के लिए, चर प्रतिस्थापन में दोहरे उद्धरण चिह्नों का उपयोग करें:

for x in x86-headers/*/C/populate.sh; do
  [ -e "$x" ] || break
  x="${x##x86-headers/}"
  x="${x%%/*}"
  echo "$x"
done

0

पथ नामों पर पैटर्न मिलान करने के लिए आप IFSशेल चर को सेट कर सकते हैं /और फिर शेल पैरामीटर विस्तार का उपयोग कर सकते हैं ।

यह सब एक उपधारा में करने से माता-पिता के खोल के वातावरण को अपरिवर्तित रखने में मदद मिलती है!

# cf. "The real Bourne shell problem",
# http://utcc.utoronto.ca/~cks/space/blog/programming/BourneShellLists

paths='
x86-headers/elf/C/populate.sh
x86-headers/gl/C/populate.sh
x86-headers/gmp/C/populate.sh
x86-headers/gnome2/C/populate.sh
x86-headers/gtk2/C/populate.sh
x86-headers/jni/C/populate.sh
x86-headers/libc/C/populate.sh
'

IFS='
'

# version 1
for x in $paths; do 
   ( IFS='/'; set -- ${x}; echo "$2" ); 
done


# version 2
set -- ${paths}
for x in $@; do 
   ( IFS='/'; set -- ${x}; echo "$2" ); 
done
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.