2013-05-23 17 views
5

İki dizgim var, foo_bar ve foo_abc_bar. Her ikisini de eşleştirmek isterim ve eğer ilki eşleşirse, = işareti ile vurgulamak istiyorum.Neden sed, isteğe bağlı bir grup yazdırmıyor?

echo 'foo_abc_bar' | sed -r 's/(foo).*(abc)?.*(bar)/\1=\2=\3/g' 
> foo==bar 

veya

echo 'foo_abc_bar' | sed -r 's/(foo).*((abc)?).*(bar)/\1=\2=\3/g' 
> foo== 

Ama çıkış yukarıda gösterdiği gibi bunların hiçbiri işe: Yani, benim tahminim oldu.

Dize içeriyorsa eşleşecek veya yalnızca atlamıyorsa eşleşecek isteğe bağlı bir grubu nasıl belirleyebilirim?

+0

Neden ifadede '. *' Kullanıyorsunuz, altçizgi herhangi bir rastgele dizeyle değiştirilebilir mi? –

cevap

8

çözüm:

echo 'foo_abc_bar' | sed -r 's/(foo)_((abc)_)?(bar)/\1=\3=\4/g' 

Neden önceki girişimleri işe yaramadı: eşleşecek teşebbüs regex (foo).*(abc)?.*(bar) için 'foo_abc_bar'(foo)'foo' maç olacak böylece

.*, açgözlü, ve daha sonra .* başlangıçta dizenin geri kalanıyla eşleşecektir ('_abc_bar'). Regex gerekli (bar) grubuna ulaşana kadar devam eder ve bu başarısız olur, bu noktada regex .* tarafından eşleştirilen karakterleri bırakarak geri dönecektir. Bu, ilk .* yalnızca '_abc_' ile eşleşene kadar gerçekleşecek, bu noktada son grup 'bar' ile eşleşecektir. Yani, yakalama grubundaki eşleşen dizideki 'abc' yerine, .* yakalamada eşleşir. Benim çözümün

Açıklama:

ilk ve en önemli şey _ ile .* yerini alacak, sen ayırıcı ne olacağını biliyorsanız rasgele herhangi bir dize maç için gerek yoktur. Yapmamız gereken bir sonraki şey, dizenin hangi kısmının isteğe bağlı olduğunu bulmaktır. 'foo_abc_bar' ve 'foo_bar' dizeleri geçerliyse, ortadaki 'abc_' isteğe bağlıdır. Bunu (abc_)? kullanarak isteğe bağlı bir gruba koyabiliriz. Son adım, 'abc' dizgisini hala yakaladığımız bir grupta, bu bölümü ek bir grupta sararak yapabileceğimizden emin olmaktır, böylece ((abc)_)? ile son buluruz. Daha sonra yedek grubu ayarlamamız gerekiyor çünkü fazladan bir grup var, bu yüzden \1=\3=\4, \2'abc_' (eşleşiyorsa) dize olacaktır. Çoğu regex uygulamasında, yakalama yapmayan bir grubu da kullanabileceğinizi ve \1=\2=\3'u kullanmaya devam edebileceğinizi, ancak sed'in yakalamayan grupları desteklemediğini unutmayın.

Alternatif:

Ben (sadece ilgilendiğiniz tam dizeleri maç olacak) en açık olduğu için regex yukarıdaki en iyi bahis olduğunu düşünüyorum. Ancak, yukarıda açıklandığı gibi, tembel tekrarlama yerine (mümkün olduğunca çok karakterle eşleşir) tembel tekrarlama (olabildiğince az karakter eşleşmesi) kullanarak da önlenebilir.

echo 'foo_abc_bar' | sed -r 's/(foo).*?(abc).*?(bar)/\1=\2=\3/g' 
1

Belki sadece kullanabilirsiniz: Sen .* için .*? değiştirerek bunu yapabilirsiniz, böylece ifadesi böyle bir şey olmazdı

echo 'foo_abc_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g' 
echo 'foo_bar' | sed -r 's/(foo|bar|abc)_?/\1=/g' 

> foo=abc=bar= 
> foo=bar= 

Bu önler foo==bar sen foo_bar ve I get Bazen maçtan önce, bazen maçtan önce ='u koyarak vurgulamak için biraz garip buldum.