Bir dosya adının bir kabuk glob deseniyle eşleşip eşleşmediğini nasıl programlı olarak söyleyebilirim?


13

Bir dize $stringbir glob desen ile eşleşecek olup olmadığını söylemek istiyorum $pattern. $stringvarolan bir dosyanın adı olabilir veya olmayabilir. Bunu nasıl yapabilirim?

Giriş dizelerim için aşağıdaki biçimleri varsayalım:

string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

Ben eğer belirleyen bir bash deyim bulmak istiyoruz $stringeşleşmesi olacağını $pattern1, $pattern2ya da başka herhangi keyfi glob desen. İşte şimdiye kadar denedim:

  1. [[ "$string" = $pattern ]]

    Bu neredeyse işe yarıyor, ancak $patternbir glob deseni olarak değil, bir dize deseni olarak yorumlanıyor.

  2. [ "$string" = $pattern ]

    Bu yaklaşımla ilgili sorun $pattern, genişletilmiş ve daha sonra dize karşılaştırması ile $stringgenişlemesi arasında gerçekleştirilmiş olmasıdır $pattern.

  3. [[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]

    Bu, yalnızca $stringvar olan bir dosya içeriyorsa çalışır .

  4. [[ $string =~ $pattern ]]

    Bu işe yaramaz, çünkü =~operatör $patternbir glob veya joker karakter kalıbı değil genişletilmiş düzenli bir ifade olarak yorumlanmasına neden olur .


2
Karşılaşacağınız sorun, bu {bar,baz}bir örüntü değil. Parametre genişletmesi. Bu konuda ince ama kritik fark {bar,baz}çok erken bir tarihte çoklu argümanlara genişletilir barve baz.
Patrick

Kabuk parametreleri genişletebilirse, bir dizenin bir globun potansiyel genişlemesi olup olmadığını söyleyebilir.
jayhendren

Bunu bir deneyin a = ls /foo/* şimdi bir
Hackaholic

1
@Patrick: bash man sayfasını okuduktan sonra foo/{bar,baz}, aslında bir brace genişletmesi (parametre genişletmesi değil) iken foo/*pathname genişletmesi olduğunu öğrendim . $stringparametre genişletmedir. Bunların hepsi farklı zamanlarda ve farklı mekanizmalarla yapılır.
jayhendren

@jayhendren @Patrick haklı ve daha sonra sorunuzun nihayetinde başlığın inanmaya yönelttiği şey olmadığını öğrendiniz. Bunun yerine, bir dizeyi çeşitli desen türleriyle eşleştirmek istersiniz. Bir glob paterniyle tam olarak eşleşmek istiyorsanız, caseifade Bash kılavuzuna göre Pathname Expansion ("globbing") gerçekleştirir.
Mike S

Yanıtlar:


8

Bu sorunun genel bir çözümü yoktur. Bunun nedeni, bash'de küme ayracı genişletmesinin (yani, {pattern1,pattern2,...}dosya adı genişletmesinin (diğer bir deyişle glob kalıpları)) ayrı şeyler olarak kabul edilmesi ve farklı koşullar altında ve farklı zamanlarda genişletilmesidir.Bash'ın gerçekleştirdiği genişletmelerin tam listesi:

  • destek genişletme
  • tilde genişleme
  • parametre ve değişken genişleme
  • komut ikamesi
  • aritmetik genişleme
  • kelime bölme
  • yol adı genişletme

Bunların yalnızca bir alt kümesini (belki de küme ayracı, tilde ve yol adı genişletmesi) önemsediğimizden, genişletmeyi kontrol edilebilir bir şekilde kısıtlamak için belirli kalıpları ve mekanizmaları kullanmak mümkündür. Örneğin:

#!/bin/bash
set -f

string=/foo/bar

for pattern in /foo/{*,foo*,bar*,**,**/*}; do
    [[ $string == $pattern ]] && echo "$pattern matches $string"
done

Bu komut dosyasını çalıştırmak aşağıdaki çıktıyı üretir:

/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar

Bu set -f, yol adı genişletmesini devre dışı bıraktığından çalışır , bu nedenle deyimde yalnızca küme ayracı genişletme ve tilde genişletme oluşur for pattern in /foo/{*,foo*,bar*,**,**/*}. Daha sonra [[ $string == $pattern ]]küme ayracı genişletmesi gerçekleştirildikten sonra yol adı genişletmesine karşı test etmek için test işlemini kullanabiliriz .


7

Ben buna inanmıyorum {bar,baz} olan bir kabuk glob desen (kesinlikle gerçi /foo/ba[rz]ise) size bilmek istiyorum ama eğer $stringmaçlar $patternyapabileceğiniz:

case "$string" in 
($pattern) put your successful execution statement here;;
(*)        this is where your failure case should be   ;;
esac

İstediğiniz kadar yapabilirsiniz:

case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*)         still no match;;
esac

1
Merhaba @mikeserv, yukarıda belirttiğim yorumlarda ve cevapta belirtildiği gibi, söylediklerinizin doğru olduğunu zaten öğrendim - {bar,baz}bir glob kalıbı değil. Sorumu dikkate alan bir çözüm buldum bile.
jayhendren

3
+1 Bu, soruyu tam olarak başlıkta ve ilk cümlede verildiği gibi cevaplar. her ne kadar soru daha sonra diğer kalıpları kabuk glob kalıplarıyla karıştırıyor.
Mike S

3

Patrick'in işaret ettiği gibi "farklı tipte" bir desene ihtiyacınız var:

[[ /foo/bar == /foo/@(bar|baz) ]]


string="/foo/bar"
pattern="/foo/@(bar|baz)"
[[ $string == $pattern ]]

Burada alıntı yapmak gerekli değildir.


Tamam, bu işe yarıyor, ama açıkçası, sorumu cevaplamıyor. Örneğin, başka bir kaynaktan gelen kalıpları ele almak istiyorum, yani kalıplar benim kontrolüm dışında.
jayhendren

@jayhendren O zaman muhtemelen gelen paterni o bash kabullerine dönüştürmelisiniz.
Hauke ​​Laging

Yani gerçekten yaptığınız tek şey sorumu "dosya adının bir ifadenin potansiyel genişlemesi olup olmadığını nasıl anlarım" dan "normal bash tarzı dosya adı kalıplarını bash tarzı genişletilmiş glob kalıplarına nasıl dönüştürebilirim" e dönüştürülür.
jayhendren

@jayhendren İstediğiniz şeyin "gerçekten yaptığınız her şeyin" imkansız olduğunu düşünmek bana biraz garip geliyor, ama belki de bu sadece yabancı bir dil. Bu yeni soruyu sormak istiyorsanız, giriş modellerinin nasıl göründüğünü açıklamanız gerekir. Belki basit bir sedişlemdir.
Hauke ​​Laging

1
@HaukeLaging doğrudur. Glob kalıplarıyla (aka "Pathname Expansion" olarak) nasıl eşleşeceklerini bulmak için buraya gelen insanlar, benim yaptığım gibi kafayı karıştırmakla yükümlüdür, çünkü başlık "kabuk glob kalıbı" diyor, ancak sorunun içeriği glob olmayan bir desen kullanıyor . Bir noktada, jayhendren farkı öğrendi, ancak ilk karışıklığı Hauke'nin yaptığı gibi cevap vermesine neden oldu.
Mike S

1

Ben grep böylece kullanırdım:

#!/bin/bash
string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

if echo $string | grep -e "$pattern1" > /dev/null; then
    echo $string matches $pattern1
fi

if echo $string | grep -e "$pattern2" > /dev/null; then
    echo $string matches $pattern2
fi

Hangi çıktıyı verir:

./test2.bsh 
/foo/bar matches /foo/*

-2

zsh içinde:

[[ $string = $~pattern ]] && print true

1
Soru, bash kabuğunun kullanılacağını belirtir.
jayhendren
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.