Bash'deki yol adlarında desen eşleşmesi


10

Bir dizindeki alt dizinler listesinde hareket etmek istiyorum. Düşünmek:

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

Bu verir

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

Ama yolunun ikinci kısmı ile denktir, yani değerlerini istiyorum elf, glvb ben lider kapalı şerit biliyorum x86-headers.

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

hangi verir

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

Ayrıca yolda daha sonraki terimlerin nasıl kaldırılacağını biliyorum. Yani bir seviye aşağı iniyorum:

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

verir

elf
gl
gmp
gnome2
gtk2
jni
libc

Ancak, bunları birleştirmeye çalışmak işe yaramaz. yani

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

verir

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

Şüphesiz bu yanlış sözdizimi, ancak doğru sözdizimini bilmiyorum. Elbette bunu yapmanın daha iyi yolları olabilir. Python kullanıyor olsaydım, /her yolu bir listeye bölmek için bölmeyi kullanırdım ve sonra ikinci öğeyi seçerdim, ama bunu bash'da nasıl yapacağımı bilmiyorum.

EDIT: Cevaplar için teşekkürler. Ayrıca şunu sormalıydım, bunu portatif olarak yapmak mümkün mü ve eğer öyleyse, nasıl?

Yanıtlar:


9

Onları bash(veya POSIXly) ile birleştiremezsiniz , iki adımda yapmanız gerekir.

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

Herhangi bir POSIX kabuğuna taşınabilir. Bourne kabuğuna taşınabilirliğe ihtiyacınız varsa (ama neden sorunuzu / bash'ınızı etiketliyorsunuz ?) Ayrıca Solaris'e ve orada /bin/shstandart yerine kullanmak zorunda kalmanız shveya 20 yıllık sistemlere geçmeniz durumunda ) , bu yaklaşımı kullanabilirsiniz (POSIX mermileriyle de çalışır):

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

(yukarıda bir değişkeni tırnaksız bırakmanın mantıklı olduğu çok nadir durumlardan biridir).

Sadece kayıt için, zsh ile:

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

( t ait ail h EAD h dosyasının EAD).

Veya pythonyaklaşımınız için:

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

İçindeki noktalı virgül i=${x#*/}; i=${i%%/*}isteğe bağlıdır, değil mi?
Dimitre Radoulov

3
Gibi @DimitreRadoulov It isteğe bağlı ama bazı kabukları bazı eski sürümleri ash/ 'dash bu durumda (Bourne kabuğu nasıl davrandığını idi) sağdan sola doğru atamaları ve neden sorununu aday olacağını.
Stéphane Chazelas

7

Parametre genişletme ifadeleri iç içe yerleştirmenize izin vermez, bu nedenle bunu bir atamayla iki adımda yapmak zorunda kalırsınız:

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

Burada dizileri kullanma seçeneğiniz var, ancak yukarıdaki PE'den daha hantal olacağını düşünüyorum:

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

Sonra harici komutları kullanmanın üçüncü seçeneği var. Kısa bir dosya listesiyle bu genellikle en az verimli seçenektir, ancak dosya sayısı arttıkça en iyi ölçeği ölçeklendirir:

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

Bu tüm mermiler için geçerli değil zsh, yuvalanmaya izin veriyor.
Stéphane Chazelas

3
@StephaneChazelas yeterince adil, ancak bu soru etiketlendi bash.
kojiro

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

Körüklü bir yapı kullanırken çok dikkatli olun:

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

Sonunda bir echo *. Bu durumda, sadece komik olduğunu, ancak senin, kök ise çok kötü olabilir CWDis /ve bazı alt dizinleri silmek istediğiniz /tmpbunları yankılanan yerine!

Bunu bash'da düzeltmek için şunları yapabilirsiniz:

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 nullglobDöngüden sonra varsayılan bash davranışını geri yüklemek için sonda dikkat edin .

Daha taşınabilir bir çözüm şunlar olabilir:

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

( ##Ve %%Parametre Değişimleri ksh88'de geçerlidir ).

Nullglob hakkında daha kapsamlı bir tartışma için bu bağlantıyı takip edin .

Adında boşluk olan bir aracı dizininiz varsa yukarıdaki 3 çözümün başarısız olduğuna dikkat edin. Bunu çözmek için değişken ikamesinde çift tırnak kullanın:

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

0

Yol adlarında taşınabilir desen eşleştirmesi yapmak için IFSkabuk değişkenini olarak ayarlayabilir /ve sonra kabuk parametresi genişletmesini kullanabilirsiniz.

Tüm bunları bir alt kabukta yapmak, ana kabuğun ortamını değiştirmeye yardımcı olur!

# 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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.