28.12.2017

Powershell ile belirtilen zamanda bir komut çalıştırma

Şöyle bir ihtiyacım var; sunucularımdan birinin bu gece yarısından sonra saat 03:00'te tekrar başlamasını istiyorum. Bunu zamanlanmış görevlerle yapabilirim. Ama powershell ile yapmak istiyorum. Açık bir powershell penceresi bırakarak şu komutları yazarsam bu mümkün:

PS> Start-Sleep -Seconds (New-Timespan -End '2099-12-29 03:00').TotalSeconds; Restart-Computer

Burada Start-Sleep cmdlet'i belli bir süre beklemeyi sağlayacak. Ne kadar bekleyeceğini New-Timespan cmdlet'i ona söyleyecek. New-Timespan cmdlet'i şu andan itibaren 29.12.2017 saat 03:00'e kadar ne kadar zaman beklemesi gerektiğini hesaplayacak. TotalSeconds ile de bu süreyi saniye cinsinden Start-Sleep cmdlet'ine verecek [1]. Bu kadar süre beklendikten sonra da Restart-Computer cmdlet'i ile sistemin tekrar başlaması sağlanacak.

Ancak bu komut dizisi, kopyala ve yapıştır amaçları için biraz tehlikeli. Yukarıdaki komutu kopyalayıp doğrudan powershell penceresine yapıştırıp orada tarih ve saati düzenleyeyim dersek doğrudan işletebilir. Daha önce orada yıl olarak 2099 yerine 2017 yazdığı ve o tarih de geçmişte kaldığı için makineyi doğrudan yeniden başlatmaya geçiyordu. Bunun için oraya yıl olarak uzak bir tarih girdim. Bir metin düzenleyicide (ör. Notepad) tarihi değiştirmeyi seçebilir, ya da kabaca kaç saat ve dakika sonra başlatılacağını hesaplayarak Start-Sleep cmdlet'ine bir elle hesaplanmış saniye miktarı verebiliriz. Örneğin 3 saat 45 dakika sonra komut çalıştırmak için

PS> Start-Sleep -Seconds ((3*60+45)*60); Restart-Computer

Hatta belki de bir zamanlanmış işler (Scheduled Jobs) nesnesi kullanabiliriz. Örneğin bugün 22:30'da çalışacak bir işlem için

PS> $baslat = New-JobTrigger -Once -At 22:30

PS> Register-ScheduledJob -Trigger $baslat -Name "Restart" -ScriptBlock {Restart-Computer -Force}

-Force parametresi olmadan olmadı.

[1] http://www.madwithpowershell.com/2013/08/sleep-until-certain-time-with-powershell.html

26.12.2017

sort komutu

Powershell'deki sort (aslında Sort-Object'in alias'ı) ile ilgilendikten komut satırındaki sort baştan biraz daha yavan gelmişti. Ama yine de çok güçlü. Temel amacı bir metin dosyasındaki verileri sıralamak. Örnek sabit uzunluklu alanlara sahip metin veriler.txt dosyamızda aşağıdaki gibi bir veri olsun:

2014-09-12 Cihan  1993
2017-10-01 Zeynep 1992
2013-05-02 Mutlu  1991


3 sütun veri var. İlk sütun bir tarih alanı. Ama tarihler metin sıralaması yapılabilmesi için yıl-ay-gün şeklinde yazılmış. Aksi takdirde gün-ay-yıl şeklinde olan bir alanda metin sıralaması yapabilmek için tarih veri tipinden anlayan, hatta varklı formatlardaki tarihleri algılayabilen bir algoritma gerekli, ama bu sort ile imkansız. İkinci sütunumuz düz metin tabanlı isimlerden oluşuyor. Üçüncü sütun ise tamsayılardan oluşan yıl bilgisi.

Görüldüğü gibi bi veriler metin dosyasında sıralanmamış şekilde yer alıyor. Bu verileri örneğin tarihe göre sıralamak için
sort veriler.txt
demek yeterli. Tarih ilk sütunda yer aldığı için sonuç aşağıdaki gibi olacaktır

2013-05-02 Mutlu  1991
2014-09-12 Cihan  1993
2017-10-01 Zeynep 1992


İkinci sütundaki isimlere göre sıralamak için
sort veriler.txt /+12
komutu işimizi görür. İsimler sütununun ilk karakteri 12'den başladığı için /+12 anahtarı ile sıralamada dikkate alınacak bölümün 12. karakterden sonrası olduğunu belirttik. Burada önemli olan şu; sort aslında sadece isimlere göre sıralamıyor, isim+yıla göre sıralıyor. Önceki örnekte de bu geçerliydi. Sort tarih+isim+yıla göre sıraladı. /+12 anahtarı ile alınan (isim+yıla göre sıralama) sonuç şöyle:

2014-09-12 Cihan  1993
2013-05-02 Mutlu  1991
2017-10-01 Zeynep 1992


Üçüncü sütundaki yıl bilgisine göre sıralamak istersek, 199x'ların 1'i 19. karakterden başladığı için
sort veriler.txt /+19
Burada yıl sütununun sağında başka sütun olmadığı için doğal olarak sıralamaya sadece yıl bilgisi dahil edildi.

2013-05-02 Mutlu  1991
2017-10-01 Zeynep 1992
2014-09-12 Cihan  1993


Sıralama varsayılan olarak küçükten büyüğe (1'den 9'a, a'dan z'ye) yapılır. Sıralamayı ters çevirmek için /R anahtarı kullanılabilir.

sort veriler.txt /+19 /R
sonuç:

2014-09-12 Cihan  1993
2017-10-01 Zeynep 1992
2013-05-02 Mutlu  1991


İlk örnekten beri vurguladığım "sıralamaya dahil edilen alan uzunluğu"nu kısıtlamak için /REC anahtarı kullanılabilir. Örneğin sıralamayı sadece ayın gününe göre yapmak için sıralmaya dahil edilecek veriyi 9'dan başlatıp 2 karakter uzunluğunda tutmalıyız:
sort veriler.txt /+9 /REC 2
Bu şekilde şu sonucu elde ederiz:

2017-10-01 Zeynep 1992
2013-05-02 Mutlu  1991
2014-09-12 Cihan  1993


Bir de bu sıralanmış çıktıyı başka bir dosyaya yazmak istersek /O anahtarını şu şekilde kullanabiliriz:
sort veriler /+9 /REC 2 /O sirali.txt
Eğer bu dosyayı Powershell'de sadece tarihe göre sıralamak (aslında tüm satıra göre) istersek şu olabilir:
PS C:\>Get-Content veriler.txt | Sort-Object
Ama bu verileri isim veya yıla göre sıralamak isteyince bunun şaşırtıcı bir şekilde daha zor olduğunu gördüm. Çünkü PowerShell'in sabit uzunluklu verileri import edecek varsayılan bir yöntem yok. Eğer dosya tipi CSV (virgülle ayrılmış değerler) olsaydı

tarih,isim,yıl
2014-09-12,Cihan,1993
2017-10-01,Zeynep,1992
2013-05-02,Mutlu,1991


bu dosyayı Import-CSV ile import edebilirdik. Dikkat, bu dosyada bir farklılık daha var: sütun başlıkları.

PS C:\> Import-Csv .\veriler.txt
tarih                      isim                          yıl
-----                      ----                          ---
2014-09-12                 Cihan                         1993
2017-10-01                 Zeynep                        1992
2013-05-02                 Mutlu                         1991


Burada boru ( | ) operatörü ile çıkışı doğrudan Sort-Object cmdlet'ine gönderebiliriz (kısaca sort).

PS C:\> Import-Csv .\veriler.txt | sort

Bu varsayılan olarak tarih alanına göre sıralar

tarih                      isim                          yıl
-----                      ----                          ---
2013-05-02                 Mutlu                         1991
2017-10-01                 Zeynep                        1992
2014-09-12                 Cihan                         1993


İstediğimiz alana göre sıralamak için tırnak içinde alan adını yazıp, sıralamayı büyükten küçüğe yapmak için ise -Descending anahtarını kullanabiliriz.

İlle de powershell ve sabit uzunluklu alanlar diyorsak önce metin dosyasını okuyup bir değişkene atmak gerek:
PS C:\> $metin = Get-Content .\veriler.txt
Daha sonra da bu içeriği CSV'ye uygun şekle çevirmek gerek. veriler.txt dosyasında sütunlar arasında birden fazla boşluk olabildiği için doğrudan boşluğu virgülle değiştirmek işe yaramaz. Bunun için önce her sütunun sadece 1 boşluk ile ayrılmasını sağlamak gerek. Yeterli sayıda şu komutu ard arda kullanmak ilk adım:
PS C:\> $metin = $metin -replace "  "," "
yukarıda -replace'ten sonra çift tırnakların arasında 2 boşluk karakteri, ikinci çift tırnakların arasında ise 1 boşluk karakteri var. Sonrasında her boşluğu virgülle değiştirmek gerek:
PS C:\> $metin = $metin -replace " ",","
Daha sonra da bu verilere bir başlık satırı eklemek gerek. Önce bir $csv değişkeni oluşturalım:
PS C:\> $csv =  @() 
Başlık satırını ekleyelim:
PS C:\> $csv += "Tarih,Isim,Yil"
Sonra buna verilerimizi ekleyelim
PS C:\> $csv += $metin
Nihayetinde de bunu ConvertFrom-CSV ile çevirelim
PS C:\> $csv = $csv | ConvertFrom-Csv
Bu noktada $csv değişkenimiz aynen bir CSV dosyasından aktarılmış gibi uygun formatta olacaktır.

Not: Türkçe karakterler (yıl başlığındaki ı harfi) her zamanki gibi sorun. Güzel gözüksün diye  düzelttim.