PowerShell Paketimde Bir Dosya Var
Yapmayı hiç bırakmadığım bir görev varsa o da dosyaları bulmaktır. Dosyaları organize etmek ve anlamlı bir biçimde görüntülemek için sürekli olarak yeni yollar geliştiriyorum. Doğal olarak PowerShell bu görev için harika bir araçtır. Get-ChildItem açıkça doğru başlangıç noktasıdır. Cmdlet, yalnızca bir klasörden dosyaları alma konusunda iyi çalışıyor ve joker karakterlerle ada ve uzantıya göre temel erken filtrelemeyi yapabiliyorum. Ancak son görevim dosyaları tarihe göre düzenlemekti ve Get-ChildItem'ın gizli çalışma şekli nedeniyle Where-Object ile geç filtrelemeye başvurmam gerekecek. Bunda yanlış bir şey yok. Ancak bu muhtemelen tekrarlayacağım bir görevse, çizim tahtasında bir PowerShell işlevi vardır. Amacım, dosyaları 1 haftalık veya 6 aylık gibi yaşlandırma gruplarına göre gruplandıracak bir işlev oluşturmaktır. Her ne kadar öncelikle son yazma zamanına dayalı yaşla ilgilensem de, benim (veya sizin) yaşlanmayı yaratım zamanına göre temellendirmeye ihtiyaç duyabilirim. Hadi kodlayalım.
Liste Oluşturma
Fonksiyonum Get-Childitem'den boru hattı girdisi alacak. Ancak dosyaları sıralayıp gruplayacağım için Get-ChildItem'deki tüm dosyalar fonksiyonum tarafından işlenene kadar bunu yapamam. Boru hattından geçerken bunları geçici olarak saklamam gerekiyor. Bir dizi nesnesi kullanabilir ve her ardışık düzen nesnesini ona ekleyebilirim. Kodum buna benzer bir şeye benzeyebilir.
Begin {
$files = @()
}
Process {
$files += $inputobject
}
End {
$files | Sort-Object -property LastWriteTime ...
}Bu yaklaşımda teknik olarak yanlış bir şey yok ve bunu yıllardır kullanıyorum. Ancak son zamanlarda genel bir listeye yönelmeye başladım. Özellikle bir [System.Collections.Generic.list[]] nesnesi. Bunun nedeni, bir diziye ekleme yaptığınızda, her öğe eklediğinizde dizinin silinip yeniden oluşturulmasıdır. Az sayıda öğe için bu sorun değil. Ama binlerce dosyayı işliyor olabilirim. Kullanım kolaylığı için hafif bir performans değişimi olacaktır. Bir diziye 3 saniyenin biraz üzerinde bir sürede neredeyse 9000 dosya ekledim. Genel listenin kullanılması bir saniyeden az sürdü.
Fonksiyonumun Begin bloğunda koleksiyon nesnesini şu şekilde tanımlayabilirim:
$list = [system.Collections.Generic.list[system.io.fileinfo]]::new()
Ancak bu söz dizimini basitleştirmek için bir kodlama tekniği kullanacağım. Fonksiyonumu tanımlayan .ps1 dosyasına, fonksiyondan önce bir Taking deyimi ekleyeceğim.
Using namespace System.Collections.GenericŞimdi Begin bloğunda tanımım çok daha basit:
$list = [List[System.IO.FileInfo]]::new()Genellikle PowerShell'e listenize ne tür bir nesnenin gireceğini söylersiniz. Benim durumumda bunun System.IO.FileInfo nesneleri olacağını biliyorum. Emin değilseniz veya birden fazla nesneye sahip olacaksanız, 'nesne'yi kullanabilirsiniz.
Liste nesnesinin burada değinmeyeceğim bir takım yöntemleri vardır. İşlem bloğunda her dosyayı listeye ekleyeceğim.
$list.Add($FilePath)End bloğuna geldiğimde, $list işlevime iletilen tüm dosyaları içerecektir. Artık kova büyümü yapabilirim.
Yaş Gruplaması
Ne tür eskitme kovaları istediğime karar vermem gerekiyordu. Yıla, aya ve gün sayısına göre karar verdim. Bunu bir parametreyle kontrol edeceğim.
[Parameter(HelpMessage = "How do you want the files organized? The default is Month.")]
[ValidateNotNullOrEmpty()]
[ValidateSet("Month","Year","Days")]
[string]$GroupBy = "Month"Ayrıca yaşlanma kararlarımı neye dayandırdığımı da kontrol etmem gerekiyordu.
[Parameter(HelpMessage = "Specify if grouping by the file's CreationTime or LastWriteTime property. The default is LastWriteTime.")]
[ValidateSet("CreationTime","LastWritetime")]
[ValidateNotNullOrEmpty()]
[string]$Property = "LastWriteTime",Listemdeki her dosya için yaş grubunu belirten bir özellik eklemem gerekiyor. Dosya nesnesine bir özellik ekleyeceğimi biliyorum.
$file | Add-Member -MemberType NoteProperty -Name AgeGroup -Value $valueDeğer, gruplandırma paketine göre hesaplanacaktır. Yıla göre gruplamak istersem, her dosya için bu kodu çalıştıracağım:
$value = $file.$property.year
$sort = "AgeGroup"Çıktının biçimlendirilmesine yardımcı olmak için fonksiyonun sonunda $sort değişkeni kullanılacaktır. Ay gruplaması için aya ve yıla ihtiyacım vardı. Ayrıca, düzgün bir şekilde sıralanabilmesi için değeri bir DateTime nesnesine dönüştürmem gerekiyordu. $Property, CreationTime veya LastWriteTime olacaktır.
$value = "$($file.$property.month)/$($file.$property.year)"
$sort = {$_.AgeGroup -as [datetime]}Özel Özellik Özel Değerler
Gün sayısına göre gruplamak en zor adımdı. OneWeek ve SixMonth gibi bir dizi kova adı tanımlamaya karar verdim. Ancak bu, sıralamanın dize değerinde olacağı ve doğru olmayacağı anlamına geliyordu. Benim çözümüm fonksiyon dosyamda bir Enum tanımlamaktı.
Enum FileAge {
YearPlus
Year
NineMonth
SixMonth
ThreeMonth
OneMonth
OneWeek
OneDay
}Neden? Çünkü bir Enum'da her değerin aslında 0'dan başlayan sayısal bir değeri vardır. Eskiyen grubu belirtmek için dizeler kullanıyor olsam da, sıralama yaptığımda örtülü sayısal değerde olacaktır. Bu numaralandırma ile toplam gün sayısına göre bir değer atayabiliyorum.
$age = New-TimeSpan -start $file.$property -end $now
$value = switch ($age.totaldays -as [int]) {
{$_ -ge 365} {[FileAge]::YearPlus ; break}
{$_ -gt 270 -AND $_ -lt 365 }{[FileAge]::Year;break}
{$_ -gt 180 -AND $_ -le 270 }{[FileAge]::NineMonth;break}
{$_ -gt 90 -AND $_ -le 180} {[FileAge]::SixMonth;break}
{$_ -gt 30 -AND $_ -le 90} {[FileAge]::ThreeMonth;break}
{$_ -gt 7 -AND $_ -le 30} {[FileAge]::OneMonth;break}
{$_ -gt 1 -And $_ -le 7} {[FileAge]::OneWeek;break}
{$_ -le 1} {[FileAge]::OneDay;break}
}
$sort = "AgeGroup"PowerShell sınıfını kullanarak bile özel bir nesne oluşturabilirdim. Ancak System.IO.FileInfo türünü kullanmaktan mutluyum. Ancak varsayılan biçimlendirme, özel özelliğim hakkında hiçbir şey bilmiyor. Kendi biçimlendirmemi oluşturmak isteyeceğimi biliyorum, bu nedenle yeni bir tür adı eklemem gerekiyor.
$file.psobject.TypeNames.insert(0,"FileAgingInfo")İleriye yönelik planlama yaparak, özel biçimlendirmemde gruplandırmayı kullanacağımı biliyorum; bu, nesnelerin sıralanması gerektiği anlamına gelir. Normalde bir fonksiyonu sıralamayı bırakırdım ama bu durumda buna ihtiyacım var.
$list | Sort-Object -Property $sort,DirectoryName,$property,NameÖnce $sort değişkenime, ardından dizin adına, ardından CreationTime veya LastWriteTime özelliğine ve son olarak dosya adına göre sıralama yapıyorum.
İşte tam fonksiyon:
#requires -version 5.1
#declaring a namespace here makes the code simpler later on
Using namespace System.Collections.Generic
#define an enumeration that will be used in a new custom property
Enum FileAge {
YearPlus
Year
NineMonth
SixMonth
ThreeMonth
OneMonth
OneWeek
OneDay
}
Function Get-FileAgeGroup {
[cmdletbinding()]
[alias("gfa")]
[OutputType("FileAgingInfo")]
Param(
[Parameter(Mandatory,Position=0,ValueFromPipeline,HelpMessage = "The path to a file. If you pipe from a Get-ChildItem command, be sure to use the -File parameter.")]
[ValidateNotNullOrEmpty()]
[System.IO.FileInfo]$FilePath,
[Parameter(HelpMessage = "Specify if grouping by the file's CreationTime or LastWriteTime property. The default is LastWriteTime.")]
[ValidateSet("CreationTime","LastWritetime")]
[ValidateNotNullOrEmpty()]
[string]$Property = "LastWriteTime",
[Parameter(HelpMessage = "How do you want the files organized? The default is Month.")]
[ValidateNotNullOrEmpty()]
[ValidateSet("Month","Year","Days")]
[string]$GroupBy = "Month"
)
Begin {
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)"
Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Grouping by $GroupBy on the $Property property"
#initialize a list to contain all piped in files
#this code is shorter because of the Using statement at the beginning of this file
$list = [List[System.IO.FileInfo]]::new()
#get a static value for now
$now = Get-Date
} #begin
Process {
Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding $FilePath"
#add each file to the list
$list.Add($FilePath)
} #process
End {
#now process all the files and add aging properties
Write-Verbose "[$((Get-Date).TimeofDay) END ] Sorting $($list.count) files"
#add custom properties based on the age grouping
foreach ($file in $list) {
switch ($GroupBy) {
"Month" {
$value = "$($file.$property.month)/$($file.$property.year)"
$sort = {$_.AgeGroup -as [datetime]}
}
"Year" {
$value = $file.$property.year
$sort = "AgeGroup"
}
"Days" {
$age = New-TimeSpan -start $file.$property -end $now
$value = switch ($age.totaldays -as [int]) {
{$_ -ge 365} {[FileAge]::YearPlus ; break}
{$_ -gt 270 -AND $_ -lt 365 }{[FileAge]::Year;break}
{$_ -gt 180 -AND $_ -le 270 }{[FileAge]::NineMonth;break}
{$_ -gt 90 -AND $_ -le 180} {[FileAge]::SixMonth;break}
{$_ -gt 30 -AND $_ -le 90} {[FileAge]::ThreeMonth;break}
{$_ -gt 7 -AND $_ -le 30} {[FileAge]::OneMonth;break}
{$_ -gt 1 -And $_ -le 7} {[FileAge]::OneWeek;break}
{$_ -le 1} {[FileAge]::OneDay;break}
}
$sort = "AgeGroup"
}
} #switch
#add a custom property to each file object
$file | Add-Member -MemberType NoteProperty -Name AgeGroup -Value $value
#insert a custom type name which will be used by the custom format file
$file.psobject.TypeNames.insert(0,"FileAgingInfo")
} #foreach file
#write the results to the pipeline. Sorting results so that the default
#formatting will be displayed properly
$list | Sort-Object -Property $sort,DirectoryName,$property,Name
Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)"
} #end
} #close Get-FileAgeGroupBiçimlendirilmiş Çıktı
Bu çalışmanın asıl amacı, yaşa göre gruplandırılmış dosyaları şu şekilde görmemi kolaylaştırmaktır:
Bu yüzden özel bir yazı ismine ve önceden sıralanmış çıktıya ihtiyacım vardı. Özel formatta bir ps1xml dosyası oluşturmak için New-PSFormatXML'i kullandım.
<!--
Format type data generated 09/17/2021 11:54:13 by PROSPERO\Jeff
This file was created using the New-PSFormatXML command that is part
of the PSScriptTools module.
https://github.com/jdhitsolutions/PSScriptTools
-->
<Configuration>
<ViewDefinitions>
<View>
<!--Created 09/17/2021 11:54:13 by PROSPERO\Jeff-->
<Name>default</Name>
<ViewSelectedBy>
<TypeName>FileAgingInfo</TypeName>
</ViewSelectedBy>
<GroupBy>
<!--
You can also use a scriptblock to define a custom property name.
You must have a Label tag.
<ScriptBlock>$_.machinename.toUpper()</ScriptBlock>
<Label>Computername</Label>
Use <Label> to set the displayed value.
-->
<PropertyName>AgeGroup</PropertyName>
<Label>AgeGroup</Label>
</GroupBy>
<TableControl>
<!--Delete the AutoSize node if you want to use the defined widths.-->
<AutoSize />
<TableHeaders>
<TableColumnHeader>
<Label>Directory</Label>
<Width>16</Width>
<Alignment>left</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>Created</Label>
<Width>24</Width>
<Alignment>left</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>Modified</Label>
<Width>23</Width>
<Alignment>left</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>Length</Label>
<Width>11</Width>
<Alignment>right</Alignment>
</TableColumnHeader>
<TableColumnHeader>
<Label>Name</Label>
<Width>10</Width>
<Alignment>left</Alignment>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<!--
By default the entries use property names, but you can replace them with scriptblocks.
<ScriptBlock>$_.foo /1mb -as [int]</ScriptBlock>
-->
<TableColumnItem>
<PropertyName>DirectoryName</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>CreationTime</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>LastWriteTime</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Length</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>Name</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>PS1 dosyamın sonunda format dosyasını PowerShell oturumuma aktarıyorum.
Update-FormatData $psscriptroot\FileAge.format.ps1xml
Şimdilik tek bir görüşüm var.
Enum kullandığım için dosyalarım düzgün bir şekilde sıralanıyor ve anlamlı çıktılar alıyorum. Format dosyasında biraz değişiklik yapmam gerekebilir.
Veya PowerShell ifademi ayarlayabilirim.
Gönderilerimin çoğunda olduğu gibi, nihai sonuç, oraya ulaşmak için kullandığım teknikler kadar önemli değil. Yorum ve sorularınızı bekliyoruz.




