27.10.2023

TCMB'den kur bilgisini Powershell ile çekmek

Merkez Bankası'nın kur bilgisini paylaştığı bir XML dosyası var

https://www.tcmb.gov.tr/kurlar/today.xml

adresinde, yıllardır değişmedi. İki tane kur bilgisini almak için her seferinde tarayıcıyı açıp adresi yazmaktansa küçük bir betik ile bakmak daha pratik geldi.

$tcmb_url = "https://www.tcmb.gov.tr/kurlar/today.xml"
[xml]$icerik = Invoke-WebRequest $tcmb_url
$icerik.Tarih_Date.Currency[0].CurrencyCode
$icerik.Tarih_Date.Currency[0].BanknoteSelling

0 endeks Amerikan Doları, 3 endeks de Avro.

Daha geniş bir görüş açısına sahip olmak için

$icerik.Tarih_Date.Currency[0]

komutu ile ne tür bir veri ile uğraştığımızı görmek istersek aşağıdaki gibi bir yapı ile karşılaşırız:

CrossOrder      : 0
Kod             : USD
CurrencyCode    : USD
Unit            : 1
Isim            : ABD DOLARI
CurrencyName    : US DOLLAR
ForexBuying     : 28.1026
ForexSelling    : 28.1532
BanknoteBuying  : 28.0829
BanknoteSelling : 28.1954
CrossRateUSD    :
CrossRateOther  :


20.10.2023

Linux terminali öğrenmek

 Linux terminal komutlarını öğrenmek için bulduğum güzel bir site var;

https://devopschops.com/blog/games-for-learning-linux/

Bu sitenin içinde denediğim ve eğlenceli bulduğum iki bağlantı ise

Command Challenge

ve

Sad Servers

boş vakitlerde denenebilir.

19.10.2023

linux'ta xargs komutu

xargs komutu, pipeline'dan veri almayı desteklemeyen komutlara bu özelliği kazandırmak için kullanılabilir. Hiçbir parametre kullanmadığımızda her biri yeni bir satıra yazılmış öğeleri boşluk ile ayrılmış şekilde aynı satıra yazar. Örnek olarak sehirler.txt dosyamızda şehir isimleri, her satırda bir tane olacak şekilde yazılmış olsun.

$ cat sehirler.txt

Bursa
Ankara
Kocaeli
Antalya
Adana

Bu şehir isimlerini tek satırda, boşluk ile ayrılmış şekilde yazmak için

$ cat sehirler.txt | xargs

kullanabiliriz. Bu elbette, xarg komutunun tüm yeteneklerini gösterecek bir örnek değil.

Mevcut klasörde dosya1.txt, dosya2.txt ve dosya3.txt adında 3 dosyamız olsun. Bunların içlerinde kaç kelime ve satır var, görmek istiyoruz.

$ ls dosya*.txt | wc

çalışmaz. Çünkü wc, pipeline input'u desteklemez. Bunu çalışır hale getirmek için

$ ls dosya*.txt | xargs wc

yapabilriz. Ya da bir log klasöründe 1 haftadan eski log dosyalarını silmek istiyoruz diyelim. Önce bu dosyaları bulalım:

$ find /var/log/ -type f -mtime +7

Bu komut ile bulunan dosyaları silmek için 

$ find /var/log/ -type f -mtime +7 | rm

işe yaramaz, çünkü rm stdin'den veri okumaz. Evet, find'in -exec parametresi ile de çözülebilir, ama örnek olması açısından şöyle bir kullanım xarg'ı açıklayabilir:

$ find /var/log -type f -mtime +7 | xargs rm

sehirler.txt dosyasının içeriğinin tek satırda yazıldığı örnekteki gibi, yukarıdaki örnek de aslında şuna eşdeğer bir işlem yapar:

$ rm log01.txt log02.txt log03.txt ...

Her log dosyasını ayrı bir rm komutuyla silmek için -n 1 kullanabiliriz

$ find /var/log/ -type f -mtime +7 | xargs -p -n 1 rm

Bu komut da şuna eşdeğer bir işlem yapar:

$ rm log01.txt
$ rm log02.txt
$ rm log03.txt
...

Bu dosyaları başka bir yere taşımak için

$ find /var/log/ -type f -mtime +7 | xargs -I % mv % /backup

Burada xargs, % yerine dosya isimlerini yerleştirir. -I parametresi, gizli -n 1 gibi davranarak aslında mv komutunun her dosya için ayrı ayrı çalıştırılmasını sağlar. Yani şuna eşdeğer bir işlem yapar:

$ mv log01.txt /backup
$ mv log02.txt /backup
$ mv log03.txt /backup
...

Dosya isimlerinde alfanümerik olmayan karakterlerin bulunması durumunda dosya isimlerini null karakteri ile ayırma şansımız var. Hem find komutunun dosya isimlerini null ile ayırt etmesini (-print0), hem de xargs komutunun bu karakterle ayrım yapmasını (-0) söyleyebiliriz:

$ find /var/log/ -type f -mtime +7 -print0 | xargs -0 rm

Paralel işlem yapılması ihtiyacı olan durumlar için xargs'ın -P gibi bir parametresi var.

12.10.2023

Google Earth'ten duvar kağıtları

Durup durup duvar kağıtlarına takılıyormuşum gibi bir his var içimde [1], [2], [3]. Ama hem masaüstü görüntümü güzelleştirmesi, hem de powershell (veya bash) pratiği olması açısından ilgilenmekten hoşlandığım bir konu.

Yeni gözdem, Google Earth ile elde edilen büyüleyici görüntülerin duvar kağıdı olarak kullanılması. Hedef sitemiz https://earthview.withgoogle.com. Sitedeki resimleri görüntülemek için açılış ekranında gözüken "Explore Images" butonuna basarak ilerlemek gerekiyor. Kaynak kodundan baktığımızda bu aslında bir buton şeklindeki bir <a> HTML tag'i. Bu tag'in href attribute'unda ilk seferde gösterilecek resimlerden birinin bağlantısı rastgele yazılı geliyor. Örneğin şu şekilde

<a class="button intro__explore" href="/camaguey-cuba-13980" title="Explore Earth Views" data-navigo>Explore images</a>

Burada /camaguey-cuba-13980 şeklinde görüntülenen href değerinin sonunda yazan 13980 bizim için önemli. Bunu okumamız lazım. Muhtemel yöntemlerden biri şu:

PS> (((iwr "https://earthview.withgoogle.com").Links | where {$_.title -eq "Explore Earth Views"} | select -expand href) -split "-")[-1]

13980

Anasayfadaki Links nesnesinin süzüp, içinden Title değeri "Explore Earth Views" olanı çekiyorum. Bunun href değerini alııp, "-" karakteri bölümlere ayırıp en sonuncusunu çekiyorum. Bunun sonucunu bir değişkene attığımızı varsayalım. Eğer bu çalışmıyorsa Invoke-WebRequest'i -UseBasicParsing ile kullanmamız gerekiyor olabilir.

Bu butona (bağlantıya) tıklandığında bir sonraki sayfada üst köşede bir indirme butonu geliyor. Bu butonun da href'inde şöyle bir indirme adresi bulunuyor:

https://earthview.withgoogle.com/download/13980.jpg

O zaman bir sonraki adımda indirmek için şunu yapmamız gerekecek

PS> iwr "https://earthview.withgoogle.com/download/13980.jpg" -Outfile "D:\wallpaper\13980.jpg"

Şimdi işi uzatan kısımları değişkenlere atıp bunu tek satırda yazalım:

PS> $url="https://earthview.withgoogle.com";$hedef=(((iwr $url).Links | where {$_.title -eq "Explore Earth Views"} | select -expand href) -split "-")[-1];iwr "$url\download\$hedef.jpg" -Outfile "D:\wallpaper\$hedef.jpg"

Sonraki adımda bunu Windows'a duvar kağıdı olarak ayarlamak için şu yazımda bahsettiğim Set-Wallpaper gibi bir fonksiyon kullanmamız gerek.

Benzer bir şekilde bu işi Linux'ta yapmak için şöyle bir dizi komut kullanılabilir:

TARGET_URL="https://earthview.withgoogle.com"
IMAGE_ID=$(curl -s ${TARGET_URL} | grep -Eio '\/download\/[0-9]{5}.jpg')
curl -s $TARGET_URL$IMAGE_ID -o 'bugunkuresim.jpg'

Tek satırlık bir kod değil, Powershell'in birebir aynısı değil, ama aynı amaca hizmet eder.

10.10.2023

Mevcut powershell sürecinin PID'sini bulma

Windows Terminal ile birden fazla sekme mümkün. Her sekme aslında yeni bir powershell.exe süreci başlatıyor. Bu süreçlerin ID'sini bulmak için powershell değişkenlerinden $PID'yi kullanabiliriz.

PS> $PID

Bir script'in veya .psm1 dosyasının içinden mevcut sürecimizi görmek istersek

PS> [System.Diagnostics.Process]::GetCurrentProcess()

kullanabiliriz.

---

https://learn.microsoft.com/tr-tr/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-5.1

Powershell ile toplu ping atma

Evet, nmap var. Ama ben bu işi Powershell ile yapmak istiyorum. Evet, firewall'ları açık olanlar cevap vermeyecek.

Hedef, mevcut yerel ağımızdaki (C sınıfı) IP adreslerine ping atarak açık olanları listelemek.

Yerel ağımızdaki IP aralığı 192.168.7.0/24 şeklinde. Son oktet 1'den başlayarak 254'e kadar değişecek.

ping işlemlerini seri olarak (biri bitmeden diğeri başlamayacak şekilde) yapmak istemiyorum. Powershell 5.1 ile paralel yapma şansım da yok. Başka ne yapabilirim? Arkaplan görevleri (background jobs)!

Tek bir hedefi pinglemek (powershell eşdeğeri Test-Connection) için arkaplan görevlerini şu şekilde kullanabilrim:

PS> Start-Job -ScriptBlock {Test-Connection -Computername 192.168.7.1 -Count 1 -Quiet}

Bu işlemi başlattıktan sonra eğer hedef bilgisayar açıksa ve ping'lere cevap verebiliyorsa 1 saniye içinde True değeri döner. Cevap vermiyorsa 4 saniye bekler ve False döner. Ama bu işlemi 254 hedef IP adresi için yapınca işler biraz değişiyor.

PS> 1..254 | % { Start-Job {Test-Connection -Comp "192.168.7.$Using:_ -Quiet -Count 1} } 

Bunun sonucunda 1'den 254'e kadar giden son oktette her bir oktet değeri için yeni bir powershell.exe süreci başlatılır, ve bu sürecin içinde Test-Connection çalıştırılır. Gördüğüm kadarıyla çok fazla bellek ve işlemci ihtiyacı oluyor. Her arkaplan görevinin sadece 1 adet ICMP paketi göndermek ve almak olduğunu düşünerek bu arkaplan görevlerinin hızlıca sonlanacağını düşünüyordum, ama gördüğüm kadarıyla öyle olmuyor. Bunun sebebini bilmiyorum, ama Windows'un görev yönetimi ile ilgili bir darboğaza sebep olduğu yönünde bir izlenim oluştu. Reddit'te  gördüğüm alternatif çözüm önerisi Test-Connection cmdlet'i ile birlikte -AsJob parametresini kullanmak:

PS> 1..254 | % { Test-Connection -Comp "192.168.7.$_" -Count 1 -AsJob } | wait-job | Receive-Job | ? {$_.ResponseTime -ne $null} | select Address, ResponseTime

Bu komutu tek parça olarak çalıştırınca C sınıfı bir subneti taramak 4 sn sürdü. Arka planda neler olduğunu anlamadım. Bu satırı ikiye ayırıp Wait-Job ve devamını ikinci bir satıra yazarsak Receive-Job komutunun bekleyen bütün görevleri listelemesi uzun sürüyor. Acaba sadece benim makinemde mi, bilmiyorum ama bu yöntem hızlı.

5.10.2023

telnet yerine ncat

Amacım, Dell switch'lerin üzerinde bir MAC adresi araması yapmak. Bunu tek tek switch'lere telnet ile bağlanarak yapabilirim. Örneğin

> telnet 192.168.7.1
User: birkullanici
Password: **************
show mac address-table | include AB:CD:EF:01:23:45
quit

gibi bir yöntemle yapmak mümkün. Ama bunu birden fazla switch için tekrarlamak yorucu. Bir betik ile yapmak istesem, telnet, bildiğim kadarıyla otomasyona açık değil. vbs scriptleri içinde SendKeys() kullanmak gibi bir şansımız var, ama onu da çok takdir ettiğimi söyleyemem.

Nihayet, başka bir yöntem buldum. telnet yerine ncat kullanarak bu işi otomatikleştirebildim. Öncelikle ncat'e göndermem gereken komutları bir string nesnesinde birleştirmeliyim. Bunu powershell'de bir değişkende toplayacağım. Önce kullanıcı adı ve şifremi yazıp daha sonra show mac address-table komutunu bu değişkene ekleyeceğim. Elbette aradığım MAC adresini de eklemem gerek. Ama Dell switch'ler MAC adresini iki nokta üst üste (:) veya tire (-) karakteriyle değil, nokta ile ayrılmış 4'lü gruplardan oluşmasını istiyor. Örneğin

AB:CD:EF:01:23:45 veya AB-CD-EF-01-23-45

değil,

ABCD.EF01.2345

olmalı. Genelde MAC adresi girişini ilk verdiğim iki formattan birinde verebildiğim için bunu betiğin içinde dönüştürmeliyim. ncat'e göndereceğim komutları içeren string değişkenini oluşturduğum betiğin ilk kısmını şu şekilde yazdım:

param(
    [Parameter(Mandatory=$true)][string]$mac
)
$mac=$mac.Replace(":","").Replace("-","")
$mymac=$mac.Insert(4,".").Insert(9,".")
$data = "birkullanici`r`nPAROLA`r`nshow mac address-table | include $mymac`r`nquit`r`n"

birkullanici switch'i yönetmek için kullandığımız kullanıcı adımız, büyük harflerle yazdığım PAROLA da parolamız. Bu işleri Windows'da powershell ile yapıyorum. Enter karakteri olarak da hem carriage return (`r) hem de line feed (`n) kullandım. Bundan sonra switch'lerimizin IP adreslerinin bir dizi (array) tipinde bir değişkende ($cihazlar) olduğunu varsayalım. İstersek bu diziyi elle doldurabilir, istersek bir dosyadan okuyabiliriz. Bu doğrultuda betiğimizin ikinci kısmını şu şekilde yazabiliriz:

foreach ($cihaz in $cihazlar) {
    $out = data | ncat -C $cihaz 23
    $out | select-string $mac
}

Bu iki parça birleştirilince her switch'in mac adres tablosunda yer alan kayıtlar sırasıyla gösterilecektir.

Eposta sunucusunu denemek

Yerel ağımızda (veya 25 tcp portu ile erişebildiğimiz bir ağda) bir eposta sunucumuz var. Bunun çalışıp çalışmadığını görmek istiyoruz. Eskiden Windows'ta gelen telnet (Linux'ta hala var) ile şu şekilde deniyorduk:

> telnet epostasunucusu.alanadi.com 25
helo metin
mail from:deneme@alanadi.com
rcpt to:alici@alanadi.com
subject:deneme
data
deneme mesaji
.
quit

Telnet artık varsayılan olarak Window'da gelmiyor. Yüklemek için "Windows Özelliklerini aç veya kapat"tan "Telnet İstemcisi"ni açmak gerek. Bunun yerine nmap paketi ile gelen ncat (netcat klonu) kullanılabilir.

> ncat -C epostasunucusu.alanadi.com 25
helo metin
mail from:deneme@alanadi.com
rcpt to:alici@alanadi.com
subject:deneme
data
deneme mesaji
.
quit

ncat, ikinci satırdan başlayarak (helo ile başlayan) quit komutuna kadar olan komutları bir metin dosyasının (smtp_komutlari.txt) içeriği olarak kaydetmemiz durumunda şu şekilde betiklerde kullanılabilir:

PS> gc .\smtp_komutlari.txt | ncat -C epostasunucusu.alanadi.com 25

Ya da Powershell'in imkanlarını kullanarak

PS> Send-MailMessage -From deneme@alandi.com -To alici@alanadi.com -Subject deneme -Body "deneme mesaji" -SmtpServer epostasunucusu.alanadi.com -Port 25

Ya da Powershell splatting kullanarak

PS> $SmtpParams = @{From="deneme@alanadi.com";To="alici@alanadi.com";Subject="deneme";"Body="deneme mesaji";SmtpServer="epostasunucusu.alanadi.com";Port=25}

PS> Send-MailMessage @SmtpParams

yapmak daha şık olabilir mi?