ITBloggen
Sveriges främsta bloggportal för oss som jobbar med it
I huvet på en powershellare

När jag höll på och skriva mitt förra inlägg tänkte jag först använda ett lite längre skript som exempel, det scriptet skrev jag i VB.NET och grupperar alla filer baserat på extension och summera ihop storlekarna. Det skriptet ser ut som nedan:

Imports System.IO

Imports System.Collections.Generic

 

Module GetFileSizesByExtension

   Sub Main(ByVal args() As String)

 

      'Check arguments

      If Not args.Length = 1 Then

         Console.WriteLine("Syntax: getfilesizesbyextensions.exe <path>")

         Return

      End If

 

      Dim dict As New SortedDictionary(Of String, Integer)

 

      'Iterate through the files

      For Each filename As String In Directory.GetFiles(args(1))

         Dim fi As New FileInfo(filename)

         Dim extension As String = fi.Extension.ToLower()

 

         If dict.ContainsKey(extension) Then

            dict(extension) += fi.Length

         Else

            dict.Add(extension, fi.Length)

         End If

      Next

      

      'Print results

      For Each extension As String In dict.Keys

         Console.WriteLine("{0,-5} - {1}", extension, dict(extension))

      Next

   End Sub

End Module

När jag satt och skrev programmet slog det mig att Powershell troligen skulle passa utmärkt för att göra motsvarande, så jag tänkte att vi skulle försöka oss på detta.

Första steget är att få ut alla filerna, inga konstigheter där:

PS# dir C:\windows\system32

Det finns ett en CmdLet med namnet Group-Object ("group") och -property används för att berätta vad man skall gruppera på. Vi testar:

PS# dir \windows\system32 | group Extension

Count Name                      Group
----- ----                      -----
   49                           {1025, 1028, 1031, 1033...}
    5 .inf                      {$ncsp$.inf, $winnt$.inf, homepage.inf, ieuinit.inf...}
    2 .cpx                      {12520437.cpx, 12520850.cpx}
 1283 .dll                      {6to4svc.dll, aaaamon.dll, aaclient.dll, acctres.dll...}
   24 .cpl                      {access.cpl, appwiz.cpl, bthprops.cpl, desk.cpl...}
  304 .exe                      {accwiz.exe, actmovie.exe, ahui.exe, alg.exe...}

Kalas, vi är på rätt väg. Låt se, nu har vi en uppsättning objekt av type GroupInfo, där varje objekt innehåller namnet på extensionen, en lista med filerna som har den extensionen, samt antalet filer. Nu behöver vi ta reda på vad summan av storlekarna för filerna är.

Hmm... Alltså, för varje GroupInfo-objekt så vill vi ta listan (som finns i propertyn Group), och för varje objekt i listan vill vi ta ut storleken.

Det finns en CmdLet med namnet Measure-Object som kan summera och räkna på information i objekt:

PS# dir c:\windows\system32 | group Extension | % { ($_.Group | measure-object Length -sum).Sum }
Measure-Object : Property "Length" cannot be found in any object(s) input.
At line:1 char:75
+ dir c:\windows\system32 | group Extension | % { ($_.Group | measure-object  <<<< Length -sum).Sum }
59520
4384
295688942
5656920
43885527
2947615
1588512

Okey, Det var en del på en gång, vi skall försöka dela upp det.

%-tecknet är förkortning för "ForEach-Object"; den CmdLet:en kör efterföljande scriptdel en gång för varje objekt (i det här fallet GroupInfo-objekt). I scriptet representeras det aktuella objektet med $_.

Så för varje GroupInfo-objekt så tar vi .Group och skickar in till Measure-Object. Där berättar vi att det är propertyn Length (den innehåller filstorleken) som vi vill summera. Measure-Objekt kommer att skicka tillbaka ett GenericMeasureInfo som bl a innehåller en Sum-property, vilken vi vill använda.

I ovanstående text ger Measure-Object ett felmeddelande för den första listan. Anledningen till detta är att "dir" ger tillbaka både FileInfo-objekt och DirectoryInfo-objekt. Det senare innehåller ingen Length-property. Så för att få bort felmeddelandet kan vi lägga in ett filter lite tidigare i kommandosträngen:

PS# dir c:\windows\system32 | ? { $_.GetType().Name -eq "FileInfo" }

? är förkortning för "Where-Object".

Nästa problem att lösa är att vi vill inte bara ha en lista med summerade storlekar. Om inte annat så vill vi ju veta vilken extension som genererat varje summering. Ett alternativ är att utöka GroupInfo-objektet med den summerings-informationen som Measure-Object gav or. Det vi kan göra är att använda "add-member":

PS# dir c:\windows\system32 | ? { $_.GetType().Name -eq "FileInfo" } | group Extension | % { $_ | add-member NoteProperty Size ($_.Group | measure-object Length -sum).Sum -pass}


Count Name                      Group
----- ----                      -----
    5 .inf                      {$ncsp$.inf, $winnt$.inf, homepage.inf, ieuinit.inf...}
    2 .cpx                      {12520437.cpx, 12520850.cpx}
 1283 .dll                      {6to4svc.dll, aaaamon.dll, aaclient.dll, acctres.dll...}
   24 .cpl                      {access.cpl, appwiz.cpl, bthprops.cpl, desk.cpl...}

Så vi har använt add-member för att lägga till en NoteProperty med namnet Size och värdet av summeringen till GroupInfo-objektet. Vi har också lagt till "-passthru" för att berätta för add-member att objektet skall skickas vidare. De flesta andra CmdLets gör detta utan att man behöver säga åt dem.

Men i listan som skrivs ut så syns inte våran Size. För att se ifall den egentligen är där så testar vi att skicka vidare objekten till Get-Member (eller lite kortare "gm"). Get-Member kommer att titta på det första av de objekten som den får, och skriva ut vilka properties och sånt som objektet har.

PS# dir c:\windows\system32 | ? { $_.GetType().Name -eq "FileInfo" } | group Extension | % { $_ | add-member NotePropert
y Size ($_.Group | measure-object Length -sum).Sum -passthru} | gm


   TypeName: Microsoft.PowerShell.Commands.GroupInfo

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       System.Boolean Equals(Object obj)
GetHashCode Method       System.Int32 GetHashCode()
GetType     Method       System.Type GetType()
get_Count   Method       System.Int32 get_Count()
get_Group   Method       System.Collections.ObjectModel.Collection`1[[System.Management.Automation.PSObject, System....
get_Name    Method       System.String get_Name()
get_Values  Method       System.Collections.ArrayList get_Values()
ToString    Method       System.String ToString()
Size        NoteProperty System.Double Size=59520
Count       Property     System.Int32 Count {get;}
Group       Property     System.Collections.ObjectModel.Collection`1[[System.Management.Automation.PSObject, System....
Name        Property     System.String Name {get;}
Values      Property     System.Collections.ArrayList Values {get;}

Där finns våran Size. Anledningen till att den inte syntes var att standard-output:en för GroupInfo inte innehåller Size, så vi får skicka vidare objekten till Format-Table ("ft") och berätta vad vi vill se:

PS# dir c:\windows\system32 | ? { $_.GetType().Name -eq "FileInfo" } | group Extension | % { $_ | add-member NotePropert
y Size ($_.Group | measure-object Length -sum).Sum -passthru} | ft Name, Count, Size -auto

Name      Count      Size
----      -----      ----
.inf          5     59520
.cpx          2      4384
.dll       1283 295688942
.cpl         24   5656920
.exe        304  43885527

Nästan klara. Det vi har kvar är att sortera informationen, och begränsa antalet rader till 10 (så att listan blir lite smidigare, texterna ovan har jag klippt för läsbarhet). Vi skickar det till Sort-Object ("sort") och sedan till Select-Object ("select") innan det skickas till Format-Table:

PS# dir c:\windows\system32 | ? { $_.GetType().Name -eq "FileInfo" } | group Extension | % { $_ | add-member NoteProperty Size ($_.Group | measure-object Length -sum).Sum -passthru} | sort Size -desc | select -first 10 | ft Name, Count, Size -auto

Name Count      Size
---- -----      ----
.dll  1283 295688942
.exe   304  43885527
.bin     4  13196206
.TMP    17   7090705
.ocx    19   7074594
.cpl    24   5656920
.nls    68   5138370
.dat    13   4468086
.scr    11   2739200

Så, vilka CmdLet:s har vi stött på nu:

  • Get-ChildItem (dir)
  • Where-Object (?)
  • Group-Object (group)
  • ForEach-Object (%)
  • Add-Member
  • Measure-Object
  • Sort-Object (sort)
  • Select-Object (select)
  • Format-Table (ft)
  • Get-Member (gm)

 

Eftersom jag är ganska ny på blog-arenan så skulle jag gärna vilja ha feedback. Skriv och berätta huruvida ni finner ovanstående fullständigt ointerresant, felaktigt eller förhoppningsvis lärorikt.

Skriv gärna ifall ni har några förbättringar på den resulterande kommando-raden.

 


Posted 07-02-2007 2:16 by bjorn.osterman
Filed under: ,

Comments

Magnus Bjork wrote re: I huvet p&#229; en powershellare
on 07-05-2007 9:43
Välkommen Björn!
Som Exchange-nerd och icke "scriptkunning" tycker jag detta är lysande, keep up the good work!

/Magnus
bjorn.osterman wrote re: I huvet p&#229; en powershellare
on 07-06-2007 22:55
Tackar.

Peppning mottages med glädje. =)

Björn Österman's Infrastructural Programming? wrote Peeking inside the PowerShell Pipe
on 07-06-2007 23:54
Pipe (|) i PowerShell anv&#228;nds f&#246;r att skriva objekt fr&#229;n ett kommando till ett annat. Det &#228;r en nyhet...
GiZmO wrote re: I huvet p&#229; en powershellare
on 07-24-2007 10:02
Mycket bra inlägg. Kul att läsa när det är upplagt på ett bra sätt och beskrivet på ett så pass enkelt sätt att även klåpare som jag begriper åtminstonde en delmängd av orden. ;)
xdpfnn wrote re: I huvet på en powershellare
on 11-15-2008 15:35

1liMkv  <a href="qhdemglgzwfe.com/.../a>, [url=http://xhwgiolkigsz.com/]xhwgiolkigsz[/url], [link=http://kvauhchjhnqh.com/]kvauhchjhnqh[/link], http://ezgunzfuspqm.com/

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Copyright 2006 - 2010 ITBloggen, Alla rättigheter reserverade