Amikor én kezdtem informatikával foglalkozni, a Windows 3.1 idejében, akkor még INI fájlokban tároltuk a beállításokat. Egy ilyen INI fájl kb. így nézett ki:
; This is a comment row
[SQLServerData]
ServerName = WS2019SQL
IP =
192.168.1.1
[DBData]
Database = MigrationData
Table = Users
Columns = Name,
Samaccountname, Department
Orgs =
Marketing, Sales, Manfacturing,
Finance,Engineering
Látható, hogy szögletes zárójelek közötti címkékkel adatcsoportokat lehetett definiálni, ezeken belül meg adatcímke-érték párokat tudtunk berakni. Ezek alapvetően mind szöveges adatok voltak, maximum az alkalmazás, ami olvasta ezeket, bizonyos értékeknél tudta, hogy azokat számmá vagy egyéb adattípussá kell alakítani. Ha egy kulcshoz tömböt akartunk rendelni, akkor simán vesszővel elválasztott módon fel kellett sorolni az értékeket. Ha nagyon sok eleme volt egy tömbnek, akkor több sorba is lehetett írni az elemeket, ilyenkor a folytatás sorban nem volt kulcs és egyenlőségjel, innen lehetett tudni, hogy az itteni adatok még az előző folytatásai. Ami még látszik, hogy az adatok „sekély” szerkezetbe szervezetők, azaz nem nagyon lehet mély, hierarchikus adatstruktúrákat tárolni.
Összeraktam egy egyszerű függvényt, amivel egy ilyen fájlt lehet olvasni és értelmeztetni:
function ParseINI {
param($path)
$config = @{}
switch -regex (Get-Content -Path $path){
"^;|^\s*$"
{continue}
"^\[([^\]]+)\]\s*$" {
$currenthive = $Matches[1].trim(); $config.$currenthive = @{}; continue
}
"^([^=]+)=([^=]+)$" {
$valuename = $Matches[1].trim()
$values = $Matches[2].trim() -split ",\s*" | Where-Object {$_ -and $_.trim()} | ForEach-Object {$_.trim()}
$config.$currenthive.$valuename = $values
continue
}
"^\s*(.+)$" {
$values = $Matches[1].trim() -split ",\s*" | Where-Object {$_ -and $_.trim()} | ForEach-Object {$_.trim()}
$config.$currenthive.$valuename += $values
continue
}
default {
Write-Error "Unknown line: '$_'"
}
}
$config
}
A függvény egy hashtáblát fog visszaadni adatcsoportonkénti, azon belül adatcímkénti kulcsokkal. A függvény érdemi része egy nagy switch ciklus, elsőként a megjegyzéseket és az üres sorokat csak átugrom, majd vizsgálom, hogy csoportjelölő címke van-e a sorban, ebből csinál a kimeneti hashbe egy kulcsot. Majd nézem, hogy adatcímke és egyenlőségjel van-e a sorban, ha igen, akkor ezt szétszedem $valuename-re és $value-re és be is tárazom a kimeneti $config-ba. A következő esetben megpróbálom vesszőnkként feldarabolni a sort és az így megtrimmelt és megszűrt adatokat hozzáteszem a legutóbbi értékekhez.
Ha ettől eltérő sort találok a fájlban, akkor hibajelzéssel kilépek. Nézzük, mit ad ez a korábbi példa fájl esetében:
PS C:\> $result = ParseINI -path C:\PSTools\config.ini
PS C:\> $result
Name Value
---- -----
DBData {Table, Database, Columns, Orgs}
SQLServerData {IP, ServerName}
PS C:\> $result.DBData
Name Value
---- -----
Table Users
Database MigrationData
Columns {Name, Samaccountname, Department}
Orgs {Marketing, Sales, Manfacturing, Finance...}
PS C:\> $result.SQLServerData
Name Value
---- -----
IP 192.168.1.1
ServerName WS2019SQL
A függvény csodálatosan értelmezte és alakította át az INI fájlomat hashtáblává. Miért nem használunk manapság ilyen fájlokat? Mert a webes technológiák elterjedésével sokkal strukturáltabb és hibatűrőbb megoldásokra volt szükség. Ha egy INI fájlból lemarad néhány sor, akkor azt beolvasáskor nem valószínű, hogy minden esetben észre tudjuk venni.
Szóval fejlődött a világ, megjelent az XML. Hogyan nézne ki egy XML formátumú konfigurációs fájl:
<!-- comment -->
<Config>
<SQLServerData>
<ServerName>WS2019SQL</ServerName>
<IP>192.168.1.1</IP>
</SQLServerData>
<DBData>
<Database>MigrationData</Database>
<Table>Users</Table>
<Columns>
<Column>Name</Column>
<Column>Samaccountname</Column>
<Column>Department</Column>
</Columns>
</DBData>
<Orgs>
<Org>Marketing</Org>
<Org>Sales</Org>
<Org>Manfacturing</Org>
<Org>Finance</Org>
<Org>Engineering</Org>
</Orgs>
</Config>
Itt már sokkal szigorúbb a szerkezet, ha valami lemarad, akkor nem lesznek jól lezárva az XML címkék és ebből tudni fogjuk, hogy valami baj van. Viszont tetszőlege mélységű hierarchiákat is tárolhatunk itt. Hogyan lehet egy ilyen fájlt beolvasni? Nagyon egyszerűen:
PS C:\> $PSConfigXML = [xml] (Get-Content C:\PSTools\config.xml)
PS C:\> $PSConfigXML
#comment Config
-------- ------
comment Config
PS C:\> $PSConfigXML.Config
SQLServerData DBData Orgs
------------- ------ ----
SQLServerData DBData Orgs
PS C:\> $PSConfigXML.Config.SQLServerData
ServerName IP
---------- --
WS2019SQL 192.168.1.1
PS C:\> $PSConfigXML.Config.DBData
Database Table Columns
-------- ----- -------
MigrationData Users Columns
PS C:\> $PSConfigXML.Config.Orgs
Org
---
{Marketing, Sales, Manfacturing, Finance...}
PS C:\> $PSConfigXML.Config.Orgs.Org
Marketing
Sales
Manfacturing
Finance
Engineering
Látható, hogy ehhez nem is kellett függvényt írnunk, ezt beépített eszközökkel is meg tudtuk oldani, egy egyszerű szöveges fájlbetöltéssel, majd ezt a szöveget XML-é alakítjuk. Miért nem szeretjük ma már ezt se? Mert túl szószátyár, minden nyitó címkéhez oda kell rakni a záró címkét. Ráadásul kis-nagybetű érzékeny a formátum, így nagyon könnyű hibát ejteni benne.
Ezután vált divatossá a JSON formátum és ehhez filozófiájában nagyon hasonló YAML és hasonló formátumok. Nézzünk egy JSON fájlt:
{
"SQLServerData": {
"ServerName": "WS2019SQL",
"IP": "192.168.1.1"
},
"DBData": {
"Database":
"MigrationData",
"Table": "Users",
"Columns": [
"Name", "Samaccountname", "Department"
],
"Orgs" : [
"Marketing", "Sales", "Manfacturing",
"Finance", "Engineering"
]
}
}
Látható, hogy ez is hierarchikus adatstruktúra. Itt a fő nehézség a vesszők használata. Igazából a sortörésnek nincs jelentősége, így hiába kezdődik valami egy új sorban, a vesszőt akkor is ki kell rakni. A másik nehézség, hogy a szabvány szerint a JSON-ba nem lehet megjegyzéseket tenni, így emberi szemmel viszonylag tömény értelmezni.
Nézzük, hogyan lehet ezt beolvasni:
PS C:\> $PSConfigJSON = get-content -path C:\PSTools\config.json | ConvertFrom-
Json
PS C:\> $PSConfigJSON
SQLServerData DBData
------------- ------
@{ServerName=WS2019SQL; IP=192.168.1.1} @{Database=MigrationData; Table=Use...
PS C:\> $PSConfigJSON.DBData
Database Table Columns Orgs
-------- ----- ------- ----
MigrationData Users {Name, Samaccountname, Department} {Marketing, Sales, M...
PS C:\> $PSConfigJSON.DBData.Orgs
Marketing
Sales
Manfacturing
Finance
Engineering
A beolvasás tehát egyszerű szövegként történik, majd a ConvertFrom-Json-al értelmeztetjük a PowerShell-el a JSON formátumú szöveget. Az így beolvasott objektum egyedi objektum típusú.
Összefoglalva: egyik idáig látott típus sem ideális:
INI: nem igazán szabványos és nem biztonságos
XML: szabványos, de nem elég tömör, sok a hibalehetőség emberi előállítás során
JSON: szabványos, de kellemetlen a vesszők kezelése, nem lehet megjegyzésekkel ellátni
A megoldás a PSD1 fájl lesz!