A nagyon haladók részére nézzünk egy olyan példát, amelyben nem egy új típushoz definiálok alaphelyzet szerinti táblázatos megjelenítő nézetet, hanem egy újabb nézetet definiálok egy már ismert típushoz. A konkrét példa legyen az, hogy szeretnék egy olyan kibővített megjelenítést is a fájlrendszer objektumaihoz, amelyben a meglevő oszlopok mellett még szeretnék egy olyat is, ahol az látszik, hogy az adott objektum konténer típusú-e (azaz mappa) és egy olyan oszlop, amelyben a fájlok esetében a kiterjesztés látszik.
Ehhez létrehoztam egy univerzális függvényt, ami létrehozza és beregisztrálja az újabb nézetet. Ennek nehézsége, hogy egyrészt egy XML fájlt kell létrehoznia a függvénynek, másrészt ki kell találni az alaphelyzet szerinti nézetet, hiszen a feladat az lenne, hogy a meglevő nézetet egészítsük ki újabb oszlopokkal. Ez nem is olyan egyszerű dolog, hiszen ha kiolvassuk az aktuális formázási definíciókat, akkor például a fájlok (System.IO.Fileinfo) és a mappákhoz (System.IO.DirectoryInfo) nincsen külön nézetdefiníció, hanem rájuk egy összevont formázó XML hat:
[6] PS C:\> Get-FormatData *file*
TypeName FormatViewDefinition
-------- --------------------
Microsoft.PowerShell.Commands.GetCou... {Counter , TableControl}
System.Diagnostics.FileVersionInfo {System.Diagnostics.FileVersionInfo...
FileSystemTypes {children ,
TableControl, children ...
System.Security.AccessControl.FileSy... {FileSecurityTable , TableControl}
Itt most a formázási definíció neve FileSystemTypes, amit én persze ki tudok találni, hogy ez vonatkozik a fájlrendszer elemeire, de az automatizáltan ezt nem lehet megtenni, így az alaphelyzet szerinti táblázat jellemzőit innen nem lehetne kiolvasni, csak úgy, hogy az összes format.ps1xml fájlba beleolvasunk, és kiderítjük, hogy melyik vonatkozik a mi adattípusainkra. Ez önmagában is elég nehéz feladat lenne.
Én másik megoldást választottam, kiíratom a kérdéses objektumokat táblázatos formában, és megfigyelem, hogy hogyan néz ki a táblázat. Ez sem volt egyszerű, mert eléggé rejtegeti a Microsoft a formázás utáni objektumok tulajdonságait, de azért sikerült!
Nézzük először a függvényt, majd hogy hogyan működik!
function Append-TableView
{
param (
$object,
$viewname
=
"custom",
$path,
$properties,
#= @{name = "PSIscontainer"; width = 5;
alignment = "left"},
# @{ name =
"extension"; width = 10; alignment = "right"}
[string[]] $exclude
)
$newprops
=
@()
$types = $object |
%{$_.gettype().fullname} | Sort-Object -Unique
$labels = (@($object)[0]|
ft |
Select-Object
-Index 0).shapeinfo.tablecolumninfolist | %{$_.label}
$widths = (@($object)[0]|
ft |
Select-Object
-Index 0).shapeinfo.tablecolumninfolist | %{$_.width}
$labels |
?{!($exclude -like $_)} | % {$i = 0} {
$newprops
+= @{name = $_; width = $widths[$i]; alignment = "left"}
$i++
}
$properties
| %{
$newprops
+= @{name = $_.name; width = $_.width;
alignment = $_.alignment}
}
$váz = @"
<?xml version="1.0"
encoding="utf-8"?>
<Configuration>
<SelectionSets>
<SelectionSet>
<Name>$($viewname)Types</Name>
<Types>
xxxtypxxx
</Types>
</SelectionSet>
</SelectionSets>
<ViewDefinitions>
<View>
<Name>$viewname</Name>
<ViewSelectedBy>
<SelectionSetName>$($viewname)Types</SelectionSetName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
xxxtchxxx
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
xxxtcixxx
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
"@
$tch = $tci = $typ = ""
$types | %{
$typ += @"
<TypeName>$_</TypeName>`r`n
"@
}
$newprops
| %{
$tch += @"
<TableColumnHeader>
$(if($_.width -gt
0){"<Width>$($_.width)</Width>"})
<Alignment>$($_.alignment)</Alignment>
</TableColumnHeader>
"@
$tci += @"
<TableColumnItem>
<PropertyName>$($_.name)</PropertyName>
</TableColumnItem>
"@
}
$váz = $váz -replace "xxxtypxxx", $typ
$váz = $váz -replace "xxxtchxxx", $tch
$váz = $váz -replace "xxxtcixxx", $tci
$váz | Set-Content
-Path "$path\$viewname.format.ps1xml" -Encoding UTF8
Update-FormatData -AppendPath "$path\$viewname.format.ps1xml"
}
Az Append-TableView függvényem négy paramétert vár. Az első az $object, ez tulajdonképpen a mintaobjektum vagy gyűjtemény, amire az új nézetet szeretnénk létrehozni. A második a $viewname, a nézet neve, a harmadik a $path, ez mutatja meg, hogy hova mentse a formázó PS1XML fájlt. A negyedik a $properties, ez egy hashtáblákból alkotott tömb, példaként megjegyzésben ott van, hogy milyen formában gondoltam ezt megadni. Egy ilyen hashtábla-elem megadja, hogy az új oszlop mely tulajdonságot tartalmazza (name), milyen szélességben (width) és milyen rendezettséggel (alignment). Az utolsó paraméter az $exclude, amellyel az eredeti nézet oszlopait lehet kihagyni.
A függvény törzsében a $types változóba összegyűjtöm, hogy a mintaként átadott objektum(ok) milyen .NET-es típushoz tartoznak, természetesen minden típusból csak egyet veszek. Ez azért kell, mert például egy Dir kimenete sem egy objektumtípust tartalmaz, így a formázó XML kifejezésünknek minden típusra érvényesnek kell lennie. Természetesen, ha annyira eltérőek az objektumok, hogy nincsenek nekik azonos tulajdonságaik, akkor nem lehet őket egy közös táblázatban láttatni, de mindenesetre a szándék meg van a függvényben erre.
Ezután jönnek a trükkök! Veszem az első mintaként átadott objektumot és kirakom Format-Table segítségével, de nem engedem ezt megjeleníteni a képernyőn, hanem elcsenem a Format-Table kimeneteként generálódó objektumot. Ezt elég nehéz vizsgálgatni, mert ha ezt továbbítom egy Format-List-be, akkor nem történik semmi, az eredeti objektum listanézetét kapjuk, nem a táblázatobjektumét. Szerencsére a Get-Member-rel valamelyest tudjuk vizsgálni:
[4] PS C:\> Get-Item .| ft | gm –membertype property
TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
Name MemberType Definition
---- ---------- ----------
autosizeInfo Property Microsoft.PowerShell.Comm...
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property System.String ClassId2e4f...
groupingEntry Property Microsoft.PowerShell.Comm...
pageFooterEntry Property Microsoft.PowerShell.Comm...
pageHeaderEntry Property Microsoft.PowerShell.Comm...
shapeInfo Property Microsoft.PowerShell.Comm...
TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupStartData
Name MemberType Definition
---- ---------- ----------
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property System.String ClassId2e4f...
groupingEntry Property Microsoft.PowerShell.Comm...
shapeInfo Property Microsoft.PowerShell.Comm...
TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Name MemberType Definition
---- ---------- ----------
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property System.String ClassId2e4f...
formatEntryInfo Property Microsoft.PowerShell.Comm...
outOfBand Property System.Boolean outOfBand ...
writeErrorStream Property System.Boolean writeError...
TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupEndData
Name MemberType Definition
---- ---------- ----------
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property System.String ClassId2e4f...
groupingEntry Property Microsoft.PowerShell.Comm...
TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEndData
Name MemberType Definition
---- ---------- ----------
ClassId2e4f51ef21dd47e99d3c952918aff9cd Property System.String ClassId2e4f...
groupingEntry Property Microsoft.PowerShell.Comm...
Látható, hogy egy táblázat nagyon sok elemből épül fel, van itt FormatStartData, GroupStartData, FormatEntryData, stb. Sajnos csak a FormatStartData objektumot lehet vizsgálgatni, a továbbiakat már nem, a PowerShell ezeket az információkat titokként őrzi. Ha például a 2., azaz az 1-es indexű GroupStartData objektumot szeretném megjeleníteni, akkor ezt kapom:
[17] PS C:\> Get-Item .| ft | Select-Object -Index 1
out-lineoutput
: Object reference not set to an instance of an object.
+ CategoryInfo : NotSpecified: (:) [out-lineoutput],
NullReferenc
eException
+ FullyQualifiedErrorId :
System.NullReferenceException,Microsoft.PowerShe
ll.Commands.OutLineOutputCommand
Szerencsére a FormatStartData.ShapeInfo tulajdonság tulajdonságaiban a fontos adatokra rátalálunk: a name, label, majd a width értékére. Ezek pont elegendőek nekünk a formázó XML kifejezés összeállításához. a $newprops változóba összegyűjtöm az össze szükséges oszlopadatot, kihagyva az ‑exclude-nál megadottakat, de belerakva a –properties-nél megadottakat. Ezután már csak a $váz-ban tárol XML vázba bele kell ezeket az információkat helyezni, majd ez egész XML-t elmenteni, majd importálni az Update-FormatData-val.
És nézzük, hogyan lehet ezt például meghívni:
Append-TableView -object (dir c:\ee) -viewname extended -path c:\ee -exclude name -properties @{name="Name"; width = 20; alignment = "left"},
@{name = "Extension"; width = 8; alignment = "left"}
És ezek után a kimenet:
[3] PS C:\> dir c:\ee | ft -View extended
Mode LastWriteTime Length Name Extensio
n
---- ------------- ------ ---- --------
d---- 2011.12.05. 21:42:48 2011.2 .2
d---- 2011.12.05. 21:42:48 2011.3 .3
d---- 2011.12.05. 21:42:47 2011.4 .4
d---- 2011.08.26. 14:17:02 al.xxx .xxx
d---- 2011.09.04. 22:35:52 Képek
d---- 2011.11.22. 19:36:25 lock
-a--- 2012.01.08. 13:12:59 1926 .format.ps1xml .ps1xml
-a--- 2011.12.30. 0:11:18 0 a.txt .txt
-a--- 2011.12.30. 0:13:24 780 első.txt .txt
-a--- 2012.01.08. 15:33:25 1689 extended.format.p... .ps1xml
-a--- 2011.12.05. 21:49:44 328 file.txt .txt
-a--- 2011.12.31. 17:21:54 793 file.txt - Shortc... .lnk
-a--- 2011.12.31. 17:06:53 0 hardlink.txt .txt
-a--- 2011.12.05. 21:49:44 328 hardlink2.txt .txt
-a--- 2011.12.31. 17:17:47 0 hardlink3.txt .txt
-a--- 2011.12.31. 17:18:02 0 hardlink4.txt .txt
-a--- 2011.12.05. 21:49:44 328 hardlink5.txt .txt
-a--- 2011.12.21. 23:09:00 10803 partnerek.csv .csv
-a--- 2011.12.21. 23:41:14 27636 partnerek2.csv .csv
-a--- 2011.12.30. 0:04:26 0 x.xml .xml
-a--- 2011.08.27. 10:45:02 13682 y.xml .xml
Sajnos a „gyári” oszlopok rendezettségét nem tudjuk kiolvasni, így azokat fixre balra rendeztem. Ha ez nem jó valami miatt, akkor –exclude-dal kihagyjuk a gyárit és újra definiáljuk immáron jobbra rendezetten.