PowerShell ile Değiştirilmiş Dosyaları Bulma
İşte PowerShell'i kullanarak sürekli uğraştığım başka bir görev. Dün hangi dosyalar üzerinde çalıştım? Veya son 48 saat içinde hangi dosyalar değiştirildi? Açıkçası, Get-ChildItem birincil komut olacak. Belirli bir klasör yolundaki bir uzantıya dayalı dosyaları yinelemeli olarak bile almak yeterince basittir. Ancak yaşa göre filtrelemek için Where-Object kullanmam ve bu senaryoda LastWriteTime özelliğini DateTime değeriyle karşılaştırmam gerekiyor. Bu arada, cmdlet'lere odaklanan PowerShell Çalışma Grubundayım ve Get-ChildItem'e DateTime filtreleme eklenmesi yönünde bir talep vardı. Maalesef .NET Framework, oluşturulan veya değiştirilen tarihler için erken filtreleme mekanizması sağlamaz. Get-ChildItem tüm dosyaları döndürdükten sonra herhangi bir filtrelemenin yapılması gerekir; bu, Where-Object'i kullanmaktan farklı değildir. Sonuç olarak Get-ChildItem üzerinde çok fazla çalışma yapılması ve performans artışı sağlanamaması nedeniyle bu özellik uygulanmayacaktır. Ama sorun değil. Bunun için bir fonksiyonum var!
Get-LastModifiedFile
Bu işlev hem Windows PowerShell 5.1 hem de PowerShell 7.x'te çalışmalıdır. İşte tam dosya.
#requires -version 5.1
Function Get-LastModifiedFile {
[cmdletbinding()]
[alias("glm")]
[OutputType("System.IO.FileInfo")]
Param(
[Parameter(Position = 0, HelpMessage = "Specify a file filter like *.ps1.")]
[ValidateNotNullOrEmpty()]
[string]$Filter = "*",
[Parameter(Position = 1, HelpMessage = "Specify the folder to search.")]
[ValidateScript({
#this will write a custom error message if validation fails
If ((Test-Path -path $_ -PathType Container) -AND ((Get-Item -path $_).psprovider.name -eq 'Filesystem')) {
return $True
}
else {
Throw "The specified Path value $_ is not a valid folder or not a file system location."
return $False
}
})]
[string]$Path = ".",
[Parameter(HelpMessage = "Specify the search interval based on the last write time.")]
[ValidateSet("Hours", "Minutes", "Days", "Months", "Years")]
[string]$Interval = "Hours",
[Parameter(HelpMessage = "Specify the number of intervals.")]
[alias("ic")]
[ValidateScript({$_ -ge 1})]
[int32]$IntervalCount = 24,
[Parameter(HelpMessage = "Recurse from the specified path.")]
[switch]$Recurse
)
Write-Verbose "Starting $($myinvocation.mycommand)"
$msg ="Searching {0} for {1} files modified in the last {2} {3}." -f (Convert-Path $Path),$filter,$IntervalCount,$Interval
Write-Verbose $msg
switch ($Interval) {
"minutes" { $last = (Get-Date).AddMinutes(-$IntervalCount) }
"hours" { $last = (Get-Date).AddHours(-$IntervalCount) }
"days" { $last = (Get-Date).AddDays(-$IntervalCount) }
"months" { $last = (Get-Date).AddMonths(-$IntervalCount) }
"years" { $last = (Get-Date).AddYears(-$IntervalCount) }
}
Write-Verbose "Cutoff date is $Last"
#remove bound parameters that don't belong to Get-ChildItem
"IntervalCount", "Interval" | ForEach-Object {
if ($PSBoundParameters.ContainsKey($_)) {
[void]$PSBoundParameters.Remove($_)
}
}
#add -File to PSBoundParameters
$PSBoundParameters.Add("file", $True)
if ($recurse) {
Write-Verbose "Recursing..."
}
else {
Write-Verbose "Searching..."
}
#get the files and filter on the LastWriteTime using the Where() method for
#better performance
(Get-ChildItem @PSBoundParameters).Where({$_.LastWriteTime -ge $last})
Write-Verbose "Ending $($myinvocation.mycommand)"
}
Ne yaptığımı anlayabilmeniz için kod boyunca yorumlar ve yardım mesajları ekledim. Ama birkaç şeye değinmek istiyorum.
Öncelikle Path parametresinde ValidateScript() niteliğini kullanıyorum.
If ((Test-Path -path $_ -PathType Container) -AND ((Get-Item -path $_).psprovider.name -eq 'Filesystem')) {
return $True
}
else {
Throw "The specified Path value $_ is not a valid folder or not a file system location."
return $False
}$Path değerinin bir kapsayıcı olduğunu ve bir FileSystem nesnesi olduğunu test ediyorum. Basitçe IF koşulunu kullanabilirdim, ancak doğrulama başarısız olursa hata mesajı üzerinde daha fazla kontrole sahip olmak istedim. Kod oldukça açık olmalı. İşte ürettiği şey.
Bu, PowerShell 7'dendir. Windows PowerShell ayrıca özel hata mesajını ve istisnanın biraz daha fazlasını görüntüler.
PSBoundParametrelerini Splatting
Fonksiyonumun bir diğer özelliği de PSBound parametrelerinin yeniden kullanılmasıdır. Parametrelerin neredeyse tamamı Get-ChildItem ile aynı olduğundan, $PSBoundParameters'ı işaretleyebilirim. Ancak geçersiz IntervalCount ve Interval parametrelerini kaldırmam gerekiyor.
"IntervalCount", "Interval" | ForEach-Object {
if ($PSBoundParameters.ContainsKey($_)) {
[void]$PSBoundParameters.Remove($_)
}
}Ayrıca Get-ChildItem File parametresini de eklemek istiyorum.
$PSBoundParameters.Add("file", $True)Teknik olarak PowerShell, parametreleri belirtmenize VE belirtmenize olanak tanır, ancak kodumu temiz tutmak istedim. Filter ve Path gibi varsayılan değerleri olan parametreler, farklı bir değer belirtmediğim sürece PSBoundParameters olarak algılanmayacaktır. Ancak bunda bir sorun yok çünkü varsayılan değerler Get-ChildItem ile aynı.
Aralığı Değiştir
İşlevim Aralık için ValidateSet() niteliğini kullanıyor ve ben varsayılan Saat değerini kullanıyorum. Bir varsayılan ayarlarken bunun doğrulama kümenize ait olduğundan emin olun. Bu, Switch ifadesinde kullanabileceğim bilinen bir değere sahip olmamı sağlar.
switch ($Interval) {
"minutes" { $last = (Get-Date).AddMinutes(-$IntervalCount) }
"hours" { $last = (Get-Date).AddHours(-$IntervalCount) }
"days" { $last = (Get-Date).AddDays(-$IntervalCount) }
"months" { $last = (Get-Date).AddMonths(-$IntervalCount) }
"years" { $last = (Get-Date).AddYears(-$IntervalCount) }
}Birden fazla olası değerim olduğundan bu, daha karmaşık bir If/ElseIf/ElseIf/Else ifadesini kullanmaktan daha temiz bir yaklaşımdır. Switch deyimi, son tarih olarak görev yapan bir DateTime değeri üretir. $IntervalCount'tan önceki tire işaretinin parametre olduğunu belirtmediğini de belirtmeliyim. Bu eksi işaretidir. $IntervalCount'tan negatif bir değer ekliyorum.
Where() Yöntemini Kullanmak
Son filtrelememde $Last değerini kullanıyorum.
(Get-ChildItem @PSBoundParameters).Where({$_.LastWriteTime -ge $last})Bu kod size biraz farklı görünebilir. Where-Object cmdlet'ini değil Where() yöntemini kullanıyorum. Bunu kullanabilirdim:
Get-ChildItem @PSBoundParameters | Where-Object {$_.LastWriteTime -ge $last}Ve aynı sonucu elde edin. Where() koleksiyonlarda veya dizilerde yerleşik bir yöntemdir. Get-ChildItem ifadesinin parantez içine alınmasının nedeni budur. Where() yöntemindeki sözdizimi Where-Object ile aynıdır. Aradaki fark performanstır. Genellikle yöntem cmdlet'i kullanmaktan daha hızlıdır. Testlerimde milisaniyelik bir farktan bahsediyoruz, ancak daha hızlı daha hızlıdır, bu yüzden buna devam edeceğim.
İşlev çıktısı, boru hattına gönderilen filtrelenmiş Get-ChildItem nesneleridir, böylece onu ihtiyacım olan şekilde kullanabilirim.
Bunlar son 6 ayda üzerinde çalıştığım dosya türleri. Uzantısı olmayan bu 153 dosya, düzeltmem gereken gizli bir .git klasöründen.
Özet
PowerShell'i bu yüzden seviyorum. Biraz çaba harcayarak ihtiyacım olanı bulmak için konsolda hızla çalıştırabileceğim bir komut satırı aracı oluşturdum. Hatta komut isteminde kullanımını daha da kolaylaştırmak için glm takma adını bile tanımladım. Umarım kendi PowerShell betiğinizde kullanabileceğiniz bir veya iki ipucu yakalamışsınızdır. Parametre doğrulama seçenekleri hakkında daha fazla bilgi edinmek için About_Functions_Advanced_Parameters yardım konusunu okuyun.



