regex etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
regex etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

2026-04-01

HTML ayrıştırma

Invoke-WebRequest cmdlet'i ile HTML içinde istenen öğelerin hızlı ayrıştırması yapılabilir. Örneğin şu sayfada yapılmış bir örnekte github üzerindeki görsellerin indirilmesi için powershell kullanılmış. Hedef sayfamız 

$url = "https://github.com/PrateekKumarSingh/CheatSheets/tree/master/Powershell"

 ile belirtilmiş. Bu sayfanın içeriği Invoke-WebRequest ile $page değişkenine atanmış.

$page = Invoke-WebRequest -UseBasicParsing -Uri $url

Bu aşamadan sonra sayfadaki bütün bağlantılara $page.Links ile ulaşmak mümkün. Hatta bir miktar biçimlendirme ile

$page.Links | Where-Object title | Select-Object -Property title, href

gibi bir tablo oluşturulmuş. Daha önce duvar kağıdı indirme için şu örneği yapmıştım, yine Links kullanarak.

Daha karışık bir örnek için live.sysinternals.com/files sayfasında yer alan araçların bir listesini oluşturmak istediğimizi düşünelim. Yine $page.Links ile sayfadaki bağlantılara erişmek mümkün ama bir de sayfada yer alan ama bir şekilde Links veya diğer property'ler ile görüntülenemeyen araçların güncellenme tarihi almak istediğimizi varsayalım. Bunun için sayfanın ham (raw) HTML kaynağını bir HTMLFile nesnesine atarak içinde adım adım ilerlememiz gerek.

İlk iş bir HTMLFile nesnesi oluşturmak

    $htmObj = New-Object -ComObject "HTMLFile"

Daha sonra $page.Content verisini bu nesnenin içine alalım.

    $htmObj.IHTMLDocument2_write($list.Content)

Yukarıdaki satır bazı durumlarda (Office yüklü olmayan bilgisayarlar veya Powershell 6+ sürümlerde)

method invocation failed because [system.__comobject] does not contain a method named 'IHTMLDocument2_write'

hatası verebilir. Bu durumda IHTMLDocument2_write satırını aşağıdaki 2 satır ile değiştirmek gerekebilir.

    $bytes = [System.Text.Encoding]::Unicode.GetBytes($page.content)
    $htmObj.write($bytes)

try/catch bloğu ile iki yöntemin birleşimini sayfanın altına yazıyorum, burası daha fazla karışmasın. 

Sayfadaki tarih ve saat verisi 

     Saturday, March 21, 2026 11:18 AM

şeklinde olduğu için bu yapıyla eşleşebilecek bir regex oluşturalım.

$pattern = '(\w+,\s+\w+\s+\d+,\s+\d+\s+\d+:\d+\s+[AP]M)\s+(\d+)\s+(\S+)'

Sayfanın kaynak kodunda yer alan header bilgisi gibi işimizi yaramayacak verilerden kurtulmak için ilk tarih saat bilgisinin geçtiği konumu bulalım.

$indxStart = ($htmObj.body.outerText | sls -Pattern $pattern).Matches[0].Index

İlk kez tarih ve saat formatının geçtiği yer baz alacağımız başlangıç noktası olacak. Buradan sayfanın sonuna kadar olan yer ile ilgileniyoruz.

    $indxEnd = ($htmObj.body.outerText).Length
    $raw = $htmObj.body.outerText.Substring($indxStart, $indxEnd-$indxStart)

Sıra bu kalıba uyan verileri ayrıştırma adımına geldi. $pattern değişkeninin içinde 3 gruplandırma kullandım, parantezler içinde. Aşağıdaki Groups bölümleri (nesneleri, dizileri, neyse) buna göre oluşturuldu.

$results = [regex]::Matches($raw, $pattern) | ForEach-Object {
    [PSCustomObject]@{
        Date     = $_.Groups[1].Value.Trim()
        Size     = [int]$_.Groups[2].Value
        FileName = $_.Groups[3].Value
    }
}

live.sysinternals.com/files sayfasında bir değişiklik olana kadar bunun çalışacağını umuyorum.

    $results | Out-GridView

Sayfada Date, Size veya FileName alanlarına ait bir veri etiketi/başlık/alan adı bulunmuyor. Bunu en son ayrıştırma aşamasında yaptım. Bu bölümdeki her alan için $pattern değişkeni içindeki bölümlerle eşleşme şöyle:

DateGroups[1](\w+,\s+\w+\s+\d+,\s+\d+\s+\d+:\d+\s+[AP]M)
SizeGroups[2](\d+)
FileNameGroups[3]   
(\S+)

---

$htmObj = New-Object -ComObject "HTMLFile"
$page = iwr -useb "live.sysinternals.com/files"

try {
    # Bu yöntem genellikle MS Office yüklü sistemlerde ve Windows PowerShell'de çalışır
    $html.IHTMLDocument2_write($content)
}
catch {
    # Office yüklü değilse veya PowerShell Core kullanıyorsanız bu yöntem çalışır
    $bytes = [System.Text.Encoding]::Unicode.GetBytes($page.Content)
    $htmObj.write($bytes)
}

 

2026-02-18

Linux sunucuyu kim yeniden başlatmış veya kapatmış

Yetkili bir kullanıcı tarafından yeniden başlatılan sunucunun journalctl loglarında şöyle bir satır görünür:

Aug 19 08:38:16 bilgisayaradi systemd-logind[1235]: The system will reboot now!

Burada bilgisayaradi ile gözüken alan kullanıcı adı değil, bilgisayar adı.

Bu olay kaydı systemd-logind birimi tarafından oluşturulmuş. Bu birim tarafından oluşturulan diğer kayıtları da görmek için

journalctl -u systemd-logind

kullanılabilir. Faydalı diğer kayıtlar şunlar olabilir: 

Nov 13 04:44:48 bilgisayaradi systemd-logind[1009]: The system will power off now! 
Nov 13 04:44:48 bilgisayaradi systemd-logind[1009]: System is powering down. 
journalctl -g "Linux version"

Burada gözüken tarih ve saatten +/- birkaç 10 saniye etrafını kısıtlayarak yapılacak bir aramada aşağıdaki regex ile kullanıcı adına erişilebilir.

journalctl --since "Nov 13 04:44:00" --until "Nov 13 04:45:00" | grep -e "Session [[:digit:]] of User"
Nov 13 04:44:47 bilgisayaradi systemd[1]: Stopping session-2.scope - Session 2 of User metin... 
Nov 13 04:44:47 bilgisayaradi systemd[1]: Stopped session-2.scope - Session 2 of User metin.

 Ayrıca sunucumuzun yeniden başlama olaylarını listelemek için de

journalctl --list-boots

komutu kullanılabilir. Sentetik bir yöntem olarak, journalctl kayıtlarında ilk yer alan 

Aug 14 14:53:07 arsiv kernel: Linux version 6.8.0-71-generic (buildd@lcy02-amd64-053) (x86_64-linux-gnu-gcc-13 (Ubuntu 13.3.0-6ub

gibi bir ifadeyi de arayabilirdim:

journalctl -g " Linux version "

 

2023-02-06

Powershell'de string'i sabit uzunluklu parçalara bölme

String verilerini farklı şekillerde dizi (array) veri tipine dönüştürmek mümkün. Örneğin her boşluk karakterini ayraç olarak kullanarak aşağıdaki $sehirler değişkeninden bir şehirler dizisi üretmek mümkün.

PS> $sehirler = "Bursa İstanbul Ankara İzmir"

PS> $sehirler -split " "
Bursa
İstanbul
Ankara
İzmir

herhangi bir ayraç karakterini kullanarak bölümleme yapılabilir. ConvertFrom-CSV ile virgülle ayrılmış değerlerden bir tablo yapmak, ya da ConvertFrom-String ile CSV'nin daha genel hali olarak herhangi bir ayraç ile ayrılmış string'den parçalar oluşturmak mümkün

PS> $sehirler | ConvertFrom-String

P1    P2       P3     P4
--    --       --     --
Bursa İstanbul Ankara İzmir

Ama sabit genişlikli bölümleme nasıl yapılır, uygun bir yöntem bulamamıştım. Şu adreste verilen yöntem amaca yönelik güzel bir yol ve üstelik -split operatörünün daha regexp kullanımına uygun olduğunu da hatırlatmak açısından faydalıydı:

PS> $sehirler -split '(.{3})' | ?{$_}

Bur
sa
İst
anb
ul
Ank
ara
İz
mir

En sondaki ?{$_} bölümü her 3 karakterlik satırdan sonra 1 boş satır ekleMEmesi için.

Bu örnek üzerinden çok anlamlı değil belki ama genel olarak sabit genişlikli string dizileri oluşturmak için kullanışlı.

2021-08-19

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.

2026-01-11 Ek: Sadece eşleşen kısmın ekrana basılması için grep ile birlikte -o anahtarı gerekir. Örneğin dosya.txt içindeki IP 10. ile balayan IP adreslerinin ekrana basılması için

> grep -oE '10\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\d[[:digit:]]{1,3}' dosya.txt

gibi. 

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

2021-08-12

Powershell ile metin dosyalarını incelemek

Grafik arayüzlü metin editörleri varken ne gerek var Powershell'e. Ama çok büyük metin dosyaları ile çalışırken bütün dosyayı açıp incelemek sorun olabilir. Çok büyük derken örneğin 250 GB. Bu kadar dosyayı açıp belleğe almak bile ciddi bir iş.

Onun yerine Get-Content ve Select-String gibi cmdlet'ler kullanılabilir mi?

Öncelikle alias'ları da kullanarak dosyanın başından 20 satırını bir inceleyelim.

cat -First 20 .\network.log

First, aslında TotalCount parametresinin alias'ı. Başka bir alias da Head.

Bir de sondan 20 satırı görelim

cat -Last 20 .\network.log

Last da aslında Tail parametresinin bir alias'ı. TotalCount ve Tail yerine First ve Last kullanmak daha kolay hatırlanabilir gibi geliyor bana. Ya da linux terimleri olarak Head ve Tail.

Başlangıçtan örneğin 5 satır atlayarak geri kalanı oku gibi bir komutu cat ve select-object'in birleşimi ile sağlayabiliriz: 

cat .\network.log | selet-object -skip 5

Buraya kadar gördüklerimizden dosyanın genel bütün satırlarının System.Net.Sockets ile başladığını gördük diyelim. Peki bununla başlamayanlar var mı? (bkz [1],[2])

sls "^(?!System.Net.Sockets)" .\network.log

Ve diyelim ki dosyanın içinde bir IIS logu benzeri bir yapı bulduk. Virgülle ayrılmış değerler. İlk alan tarih, daha sonra saat, kaynak ve hedef IP adresleri, kaynak ve hedef portları gibi:

2021-08-10,09:38:22,192.168.7.3,8.8.8.8,1356,443

Bu satırlardan da sadece kaynak IP adresi ve kaynak hedef portu ile ilgileniyoruz. Bu alanları alıp ekrana basmak için kullanabileceğimiz ConvertFrom-String cmdlet'i var. -Delimiter parametresi ile verilen karakterlerle ayrılmış alanları ayrıştırıp P1'den P(N)'e kadar alan isimlerini otomatik yaratıp kullanımımıza sunuyor. Bizim örneğimizde kaynak IP ile hedef portu 4. ve 6. alanalar olduğu için bunları aşağıdaki gibi süzebiliriz:

sls "^(?!System.Net.Sockets)" .\network.log | ConvertFrom-String | select P4,P6

P4        P6
-------   ---
8.8.8.8   443
1.1.1.1   443

gibi bir sonuç elde edilir.

 Dosyanın içinde 192.168 ile başlayan IP adresleri var mı diye bir bakalım:

sls "192\.168\.\d{1,3}\.\d{1,3}" .\network.log

Ya da tarih (örneğin 2021-08-10 formatında) yazıyor mu hiç:

sls "\d{4}-\d{2}-\d{2}" .\network.log

Belki de tarih formatımız 2021.08.10 veya 20210810 da olabilir:

sls "\d{4}.?\d{2}.?\d{2}" .\network.log

Tüm bunları yaparken bütün dosyayı değil de ilk 1000 veya son 1000 satır üzerinden işlem yapmak da isteyebiliriz. Bunun için "|" operatörü ile cmdlet'leri bağlayabiliriz:

cat -First 1000 .\network.log | sls "\d{4}.?\d{2}.?\d{2}"

---

[1] https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.1

[2] https://www.tutorialspoint.com/powershell/powershell_regex.htm