19.08.2021

Regular Expressions

Bu konu bir blog yazısında anlatılacak kadar kısa bir konu değil; farkındayım. Ama konunun ana hatlarını yine de özetlemek istiyorum.

Bir metnin içinde (düzyazı, program, bir klasördeki dosya içerikleri, URL dizileri, dosys sistemi yolları vs) bir "dünya" var mı yok mu aramak istiyoruz. Örneğin bir klasördeki txt uzantılı dosyaların içinde. Bunu powershell ile yapmanın yolu

> Select-String -Pattern "dünya" -Path *.txt

Bu arama bize bazı sonuçlar döner. Ama diyelim ki dosyaların bazılarında "ü" yerine "u" kullanıldığını farkettik. Bu durumda "dunya" anahtar kelimesini de içerecek şekilde aramamızı değiştirelim. Ayrıca varsayılan parametre sıralaması ve alias kullanımıyla komutu biraz kısalatalım.

> sls "dünya|dunya" *.txt

"|" operatörü VEYA işlemi görür. Burada değişen 1 harf olduğu için bunu ü VEYA u şeklinde de yapabilirdik.

> sls "d[u|ü]nya" *.txt

Köşeli parantezler, bir karakter kümesi belirtmek için kullanılır. Örneğin sadece d,u,ü,n,y ve a'dan oluşan 5 veya 6 harfli kelimeleri bulmak istersek

> sls "[adnuüy]{5,6}" *.txt

yazabiliriz. Bu satırla sadece dünya ve dunya anahtar kelimelerini bulmakla kalmayacağız, ayrıca içinde herhangi bir sırada bu harflerin 5 veya 6'sı geçen başka kelimeleri de bulacağız. Ayrıca bunlar tam kelimeler de olmayacak, kelimenin herhangi bir bölümünde bu harflerin 5 veya 6'sının geçmesi yeterli olacak.

Bazı özel operatörleri listeleyelim:

?Önceki karakter 0 veya 1 kez geçecek
*Önceki karakter 0 veya daha fazla geçecek.
+Önceki karakter 1 veya daha fazla geçecek.
{n,}Önceki karakter n veya daha fazla geçecek.
{n,m}Önceki karakter en az n kere, en fazla m kere geçecek. Arasındaki sayılarda da olabilir.
. Yeni satır karakteri hariç her tekil karakteri bulur.
[a-z]Yalnızca "a" ve "z" arazındaki karakterleri bulur.
[^abc]a, b ve c olmayan her karakteri bulur.
^Satırın başını bulur.
$Satırın sonunu bulur.
( )Gruplandırma için. Parantezin içindeki ifade hatırlanır ve daha sonra \n veya $n gibi bir operatör ile kullanılabilir.
(?:)Aynen ( ) gibi gruplandırma için kullanılır, ama parantezin içindeki ifade hatırlanmaz
|veya operatörü
\wtüm harfleri bulur.
\Wharf olmayan karakterleri bulur.
\sBoşluk karakterlerini bulur.
\SBoşluk olmayan karakterleri bulur.
\dSayıları bulur.
\DSayı olmayanları bulur.
\AString'in başını bulur.
\ZString'in sonunu bulur. Bir yeni satır karakteri varsa ondan hemen öncesini bulur.
\zString'in sonunu bulur.Yeni satır karakterini de atlar.
\GEn son aramanın bulduğu noktanın bir sonrasını bulur.
\bKelime sınırlarını bulur.
\BKelime olmayan sınırları bulur\.

Bir string değişkenimiz olsun, aşağıdaki gibi

> $str1 = "Bugün günlerden Pazartesi"

Regular expression ifadelerimizin nasıl çalıştığını bu string üzerinde deneyerek görelim. Powershell'de -match  operatörü regular expression kullanır. Ama -like kullanmaz. Değiştirme operatörü -replace de regular expression kullanır. Değişkenin içinde "gün" geçiyor mu bakalım:

> $str1 -match "gun"

False

Sonuç altta "False" olarak verildi. "u" ve "ü" alternatifli olarak deneyelim:

> $str1 -match "g[u|ü]n"

True

Bu şekilde bir eşleşme bulundu. Pazartesi geçen kelimeleri Salı ile değiştirmek için

> $str1 -replace "Pazartesi","Salı"

Bugün günlerden Salı

Çok karşılaşılabilecek bir şey, ay\gün\yıl şeklinde yazılan tarihlerin gün.ay.yıl şeklinde çevrilmesi olabilir. Bunun için back reference denen gruplandırma operatörü olarak yukarıdaki tabloda listelediğim parantezleri kullanmalıyız. İşlem şu şekilde olacak, 2 haneli ay bilgisi bulunacak, 2 haneli gün bilgisi bulunacak, bunlar yer değiştirilecek ve ayraç olarak kullanılan "\" yerine "." konacak ve sonrasında gelen 4 haneli yıl bilgisi değiştirilmeyecek. Bunun için bulduğu gün, ay ve yıl verilerini hatırlaması gerekecek.

> $str1 = "bugün 11\20\1974"

> $str1 -replace '(\d{2})\\(\d{2})\\(\d{4})','$2.$1.$3'

bugün 20.11.1974

Burada çift tırnak yerine tek tırnak kullanmaya dikkat.

Çok sık yaşadığım bir konu, bir klasörde bir dosya arıyorum. Dosya adında geçen bir anahtar kelimeye göre aramak kolay. Ama 2 anahtar kelimem var ve hangisi önce gelecek, hangisi sonra gelecek, aralarında başka kelime olacak mı, bu ayrıntılar önemli değil. Sadece bu 2 anahtar kelimenin dosya adında geçmesini istiyorum. Bunun için yapılması gereken [3]:

> dir * | where {$_.Name -match "(?=.*format)(?=.*powershell)"}

Benzer şekilde bir klasördeki dosyaların içinde de geçmesini istediğim 2 anahtar kelime için arama yapabilirim [9]:

> sls "(?=.*black)(?=.*star)" *-d.txt

Ekleme  2022-07-13: linux komut satırı aracı grep'in alışkanlıkları biraz farklı. farketebildiğim istisnaları şöyle:

\d    ->  [[:digit:]]

\w   ->  [[:blank:]]

ayrıca [8]'de anlatıldığı gibi şu "karakter sınıfları" da var:

[[:alpha:]]        Alfabetik karakterler

[[:alphanum:]]    Alfanumberik karakterler

[[:lower:]]        küçük harfler

[[:upper:]]        büyük harfler

Bunlara "genişletilmiş düzenli ifadeler" (extended regular expressions) denmiş. Bunları kullanmak için grep'e -E anahtarını sağlamamız istenmiş. Örneğin Manjaro'da linux çekirdeği kurulma tarihlerini /var/log/pacman.log dosyasından süzmek için

> grep -E "installed linux[[:digit:]]{2,3}[[:blank:]]" /var/log/pacman.log

kullandım. Her yeni çekirdek kurulumunda installed linux, güncellemesinde ise upgraded linux, sonrasında ise duruma göre  2 veya 3 karakterlik sürüm numarasını yazıyordu. Yeni çekirden yardımcı paketlerle geldiğinden bir tire karakteriyle bu yardımcı karekterler de listelenmesin diye [[:blank:]] de kullandım.

Ek 2023-06-04: Bazen de grep ile "içine geçmeyen"leri bulmak gerekir. Bu durum için grep için -v anahtarı önerilmiş. Şöyle olabilir:

> grep "icinde_gesin" /var/log/birdosya | grep -v "gecmesin"

Ek 2023-10-17: İçinde IP adresi olan bir string değişkenimiz var. Bu string'in içinde geçen IP adresini görmek istiyoruz. Yöntemlerden biri

PS> ($metin | select-string -Pattern "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})").Matches[0].Value

diğeri de

PS> $metin -match "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})"
PS> $matches[0]

---

[1] https://en.wikipedia.org/wiki/Regular_expression

[2] https://regexr.com/

[3] https://www.baeldung.com/string-contains-multiple-words

[4] https://www.regular-expressions.info/

[5] https://www3.ntu.edu.sg/home/ehchua/programming/howto/Regexe.html

[6] https://regex101.com/

[7] https://www.geeksforgeeks.org/write-regular-expressions/

[8] https://linuxize.com/post/regular-expressions-in-grep/

[9] https://www.rexegg.com/regex-lookarounds.html

Hiç yorum yok: