Het opslaan van gegevens in bestanden is een veel voorkomende taak bij het werken met PowerShell. Er zijn wellicht meer opties dan u beseft. Laten we beginnen met de basis en daarna werken aan de meer geavanceerde opties.
- Index
- Werken met bestandspaden
- Test-Path
- Split-Path
- Join-Path
- Resolve-Path
- Opslaan en lezen van gegevens
- Basic redirection met Out-File
- Opslaan van tekstgegevens met Add-Content
- Importeren van gegevens met Get-Content -Raw
- Gegevens op basis van kolommen opslaan met Export-CSV
- -NoTypeInformation
- Gegevens over rijke objecten opslaan met Export-CliXml
- Gestructureerde gegevens opslaan met ConvertTo-Json
- Andere opties en details
- Get-Content -ReadCount
- Sneller lezen met System.IO.File
- Schrijven met System.IO.StreamWriter
- Opslaan van XML
- Korte opmerking over codering
- Wrapping up
Werken met bestandspaden
We beginnen met het laten zien van de commando’s voor het werken met bestandspaden.
Test-Path
Test-Path
is een van de bekendere commando’s als je met bestanden gaat werken. Hiermee kunt u een map of een bestand testen voordat u het probeert te gebruiken.
If( Test-Path -Path $Path ){ Do-Stuff -Path $Path}
Split-Path
Split-Path
neemt een volledig pad naar een bestand en geeft u het pad naar de bovenliggende map.
PS:> Split-Path -Path 'c:\users\kevin.marquette\documents'c:\users\kevin.marquette
Als u het bestand of de map aan het einde van het pad nodig hebt, kunt u het -Leaf
-argument gebruiken om het te krijgen.
PS:> Split-Path -Path 'c:\users\kevin.marquette\documents' -Leafdocuments
Join-Path
Join-Path
kunt u map- en bestandspaden samenvoegen.
PS:> Join-Path -Path $env:temp -ChildPath testingC:\Users\kevin.marquete\AppData\Local\Temp\testing
Ik gebruik dit altijd als ik locaties samenvoeg die in variabelen zijn opgeslagen. Je hoeft je geen zorgen te maken over hoe je met de backslash omgaat, want dit regelt het voor je.
Resolve-Path
Resolve-Path
geeft je het volledige pad naar een locatie. Het belangrijkste is dat het jokertekens voor u zal uitbreiden.
Resolve-Path -Path 'c:\users\*\documents'Path----C:\users\kevin.marquette\DocumentsC:\users\Public\Documents
Dit is een opsomming van alle documentmappen van lokale gebruikers.
Ik gebruik dit meestal voor alle padwaarden die ik als gebruikersinvoer in mijn functies krijg die meerdere bestanden accepteren. Ik vind het een gemakkelijke manier om jokertekens toe te voegen aan parameters.
Opslaan en lezen van gegevens
Nu we al die helper CmdLets uit de weg hebben, kunnen we het hebben over de opties die we hebben voor het opslaan en lezen van gegevens.
Ik gebruik de $Path
en $Data
variabelen om je bestandspad en je data in deze voorbeelden weer te geven. Ik doe dit om de voorbeelden netter te houden en het geeft beter weer hoe je ze in een script zou gebruiken.
Basis omleiding met Out-File
PowerShell werd geïntroduceerd met Out-File
als de manier om gegevens op te slaan in bestanden. Hier is hoe de hulp daarover eruit ziet.
Get-Help Out-File<#SYNOPSIS Sends output to a file.DESCRIPTION The Out-File cmdlet sends output to a file. You can use this cmdlet instead of the redirection operator (>) when you need to use its parameters.#>
Voor iedereen die uit een batchbestand komt, is Out-File
de basisvervanger voor de omleidingsoperator >
. Hier is een voorbeeld van hoe u het kunt gebruiken.
'This is some text' | Out-File -FilePath $Path
Het is een basiscommando en we hebben het al een lange tijd. Hier is een tweede voorbeeld dat enkele beperkingen laat zien.
Get-ChildItem | Select-Object Name, Length, LastWriteTime, Fullname | Out-File -FilePath $Path
Het resulterende bestand ziet er zo uit als het wordt uitgevoerd vanuit mijn temp-map:
Je kunt zien dat de laatste kolom met waarden is ingekort. Out-File
verwerkt objecten voor de console maar stuurt de uitvoer door naar een bestand. Alle problemen die je hebt om iets te formatteren in de console zullen verschijnen in je uitvoerbestand. Het goede nieuws is dat we hier veel andere opties voor hebben die ik hieronder zal behandelen.
Tekstgegevens opslaan met Add-Content
Ik persoonlijk gebruik Out-File
niet en geef de voorkeur aan de Add-Content
en Set-Content
commando’s. Er is ook een Get-Content
commando dat erbij hoort om bestandsgegevens te lezen.
$data | Add-Content -Path $PathGet-Content -Path $Path
Add-Content
maakt bestanden aan en voegt ze toe aan bestanden. Set-Content
maakt en overschrijft bestanden.
Dit zijn goede all-purpose commando’s, zolang prestaties geen kritieke factor zijn in je script. Ze zijn prima voor individuele of kleine inhoudsaanvragen. Voor grote gegevensverzamelingen, waarbij de prestaties belangrijker zijn dan de leesbaarheid, kunnen we ons wenden tot het .Net framework. Ik kom hier nog op terug.
Gegevens importeren met Get-Content -Raw
Get-Content
is het goto commando voor het lezen van gegevens. Standaard leest dit commando elke regel van het bestand. Je eindigt met een array van strings. Dit geeft ook elke string netjes door de pijp.
De -Raw
parameter zal de gehele inhoud binnenhalen als een multi-line string. Dit werkt ook sneller omdat er minder objecten worden gemaakt.
Get-Content -Path $Path -Raw
Kolomgebaseerde gegevens opslaan met Export-CSV
Als u ooit gegevens voor Excel moet opslaan, is Export-CSV
uw startpunt. Dit is goed voor het opslaan van een object of gestructureerde basisgegevens die later kunnen worden geïmporteerd. Het CSV-formaat is door komma’s gescheiden waarden in een tekstbestand. Excel is vaak de standaardviewer voor CSV-bestanden.
Als u Excel-gegevens in PowerShell wilt importeren, slaat u deze op als CSV en vervolgens kunt u Import-CSV
gebruiken. Er zijn andere manieren om het te doen, maar dit is verreweg de gemakkelijkste.
$data | Export-CSV -Path $PathImport-CSV -Path $Path
-NoTypeInformation
Export-CSV
zal type-informatie invoegen in de eerste regel van de CSV. Als u dat niet wilt, kunt u de parameter -NoTypeInformation
opgeven.
$data | Export-CSV -Path $Path -NoTypeInformation
Rijke objectgegevens opslaan met Export-CliXml
Het Export-CliXml
commando wordt gebruikt om volledige objecten op te slaan in een bestand en ze vervolgens weer te importeren met Import-CliXml
. Dit is voor objecten met geneste waarden of complexe datatypes. De ruwe data zal een verbose geserialiseerd object in XML zijn. Het mooie is dat je een object in het bestand kunt opslaan en wanneer je het importeert, krijg je dat object weer terug.
Dit geserialiseerde formaat is niet bedoeld om direct bekeken of bewerkt te worden. Hier is hoe het date.clixml
bestand eruit ziet:
Probeer het maar niet te begrijpen. Het is niet de bedoeling dat je er in gaat graven.
Dit is nog een commando dat ik niet vaak gebruik. Als ik een geneste of hiërarchische dataset heb, dan is JSON mijn favoriete manier om die informatie op te slaan.
Gestructureerde gegevens opslaan met ConvertTo-Json
Wanneer mijn gegevens genest zijn en ik ze misschien met de hand wil bewerken, dan gebruik ik ConvertTo-Json
om ze naar JSON te converteren. ConvertFrom-Json
zal het weer omzetten in een object. Deze commando’s kunnen zelf geen bestanden opslaan of uitlezen. Daarvoor moet u Get-Content
en Set-Content
gebruiken.
Er is een belangrijk ding om op te merken bij dit voorbeeld. Ik heb een gebruikt voor mijn
$Data
maar ConvertFrom-Json
geeft in plaats daarvan een . Dit is misschien geen probleem, maar er is geen eenvoudige oplossing voor.
Ook het gebruik van de Get-Content -Raw
in dit voorbeeld. ConvertFrom-Json
verwacht één string per object.
Hier volgt de inhoud van het JSON-bestand van hierboven:
{ "Address": { "State": "California", "Street": "123 Elm" }}
Je zult merken dat dit lijkt op de oorspronkelijke hashtable. Dit is waarom JSON een populair formaat is. Het is gemakkelijk te lezen, te begrijpen en te bewerken indien nodig. Ik gebruik dit de hele tijd voor configuratie bestanden in mijn eigen projecten.
Andere opties en details
Al deze CmdLets zijn eenvoudig om mee te werken. We hebben ook nog wat andere parameters en toegang tot .Net voor meer opties.
Get-Content -ReadCount
De -ReadCount
parameter op Get-Content
definieert hoeveel regels die Get-Content
in een keer zal lezen. Er zijn enkele situaties waarin dit de geheugen-overhead van het werken met grotere bestanden kan verbeteren.
Dit omvat in het algemeen het pipen van de resultaten naar iets dat ze kan verwerken als ze binnenkomen en de invoergegevens niet hoeft te bewaren.
$dataset = @{}Get-Content -Path $path -ReadCount 15 | Where-Object {$PSItem -match 'error'} | ForEach-Object {$dataset += 1}
Dit voorbeeld telt hoe vaak elke fout in de $Path
opduikt. Deze pijplijn kan elke regel verwerken terwijl deze uit het bestand wordt gelezen.
Je hebt misschien geen code die hier vaak gebruik van maakt, maar dit is een goede optie om je bewust van te zijn.
Sneller lezen met System.IO.File
Het gebruiksgemak dat de CmdLets bieden, kan ten koste gaan van de ruwe prestaties. Het is klein genoeg dat je het niet zult merken bij het meeste scriptwerk dat je doet. Wanneer de dag komt dat je meer snelheid nodig hebt, zul je merken dat je je wendt tot de native .Net commando’s. Gelukkig zijn deze eenvoudig om mee te werken.
::ReadAllLines( ( Resolve-Path $Path ) )
Dit is net als Get-Content -Path $Path
in die zin dat je eindigt met een verzameling vol met strings. U kunt de gegevens ook lezen als een tekenreeks met meerdere regels.
::ReadAllText( ( Resolve-Path $Path ) )
De $Path
moet het volledige pad zijn, anders wordt geprobeerd het bestand op te slaan in uw C:\Windows\System32
map. Daarom gebruik ik in dit voorbeeld Resolve-Path
.
Hier volgt een voorbeeld Cmdlet dat ik rond deze .Net aanroepen heb gebouwd: Import-Content
Schrijft met System.IO.StreamWriter
Op diezelfde opmerking kunnen we ook System.IO.StreamWriter
gebruiken om gegevens op te slaan. Het is niet altijd sneller dan de native Cmdlets. Deze valt duidelijk onder de regel dat als de prestaties van belang zijn, je het moet testen.
System.IO.StreamWriter
is ook een beetje ingewikkelder dan de native CmdLets.
try{ $stream = ::new( $Path ) $data | ForEach-Object{ $stream.WriteLine( $_ ) }}finally{ $stream.close()}
We moeten een StreamWriter
openen naar een $path
. Dan lopen we de gegevens door en slaan elke regel op in de StreamWriter
. Als we klaar zijn, sluiten we het bestand. Ook hier is een volledig pad nodig.
Ik moest hier een foutafhandeling omheen zetten om er zeker van te zijn dat het bestand gesloten werd als we klaar waren. Je kunt hier een catch
toevoegen voor een aangepaste foutafhandeling.
Dit zou heel goed moeten werken voor string-data. Als u problemen ondervindt, kunt u de .ToString()
methode aanroepen op het object dat u naar de stream schrijft. Als je meer flexibiliteit nodig hebt, weet dan dat je het hele .Net framework tot je beschikking hebt op dit punt.
Opslaan XML
Als je met XML bestanden werkt, kun je de Save()
methode op het XML object aanroepen.
$Xml = "<r><data/></r>"$Path = (join-path $pwd 'File.xml')$Xml.Save($Path)
Net als bij de andere .Net-methoden in System.IO moet u het volledige pad naar het bestand opgeven. Ik gebruik $pwd
in dit voorbeeld omdat het een automatische variabele is die het resultaat bevat van Get-Location
(lokaal pad).
Quick note on encoding
De bestandscodering is de manier waarop de gegevens in binair worden omgezet wanneer ze op schijf worden opgeslagen. Meestal werkt het gewoon, tenzij je veel cross platform werk doet.
Als je tegen problemen aanloopt met de encoding, ondersteunen de meeste CmdLets het specificeren van de encoding. Als u de codering voor elk commando standaard wilt instellen, kunt u de $PSDefaultParameterValues
hashtable als volgt gebruiken:
U kunt meer vinden over het gebruik van PSDefaultParameterValues in mijn post over Hashtables.
Wrapping up
Het werken met bestanden is zo’n veelvoorkomende taak dat u de tijd moet nemen om deze opties te leren kennen. Hopelijk heb je iets nieuws gezien en kun je dit gebruiken in je eigen scripts.
Hier zijn twee functies die de ideeën waar we het over hadden implementeren