Powershell: De vele manieren om naar bestanden te lezen en te schrijven

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

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *