Powershell: I molti modi per leggere e scrivere su file

Salvare dati su file è un compito molto comune quando si lavora con PowerShell. Ci possono essere più opzioni di quanto si pensi. Iniziamo con le basi e lavoriamo sulle opzioni più avanzate.

  • Index
  • Lavorare con i percorsi dei file
    • Test-Path
    • Split-Path
    • Join-Path
    • Resolve-Path
  • Salvare e leggere i dati
    • Riindirizzamento di base con Out-File
    • Salvare dati di testo con Add-Content
    • Importare dati con Get-Content -Raw
    • Salvare dati basati su colonne con Export-CSV
      • -NoTypeInformation
    • Salvare dati ricchi di oggetti con Export-CliXml
    • Salvare dati strutturati con ConvertTo-Json
  • Altre opzioni e dettagli
    • Get-Content -ReadCount
    • Lettura più veloce con System.IO.File
    • Scrittura con System.IO.StreamWriter
    • Salvataggio di XML
    • Piccola nota sulla codifica
  • In chiusura

Lavorare con i percorsi dei file

Iniziamo mostrandovi i comandi per lavorare con i percorsi dei file.

Test-Path

Test-Path è uno dei comandi più conosciuti quando si inizia a lavorare con i file. Ti permette di testare una cartella o un file prima di provare ad usarlo.

If( Test-Path -Path $Path ){ Do-Stuff -Path $Path}

Split-Path

Split-Path prenderà un percorso completo di un file e ti darà il percorso della cartella madre.

PS:> Split-Path -Path 'c:\users\kevin.marquette\documents'c:\users\kevin.marquette

Se hai bisogno del file o della cartella alla fine del percorso, puoi usare l’argomento -Leaf per ottenerlo.

PS:> Split-Path -Path 'c:\users\kevin.marquette\documents' -Leafdocuments

Join-Path

Join-Path può unire percorsi di cartelle e file insieme.

PS:> Join-Path -Path $env:temp -ChildPath testingC:\Users\kevin.marquete\AppData\Local\Temp\testing

Lo uso ogni volta che devo unire posizioni che sono memorizzate in variabili. Non dovete preoccuparvi di come gestire il backslash perché questo se ne occupa per voi. Se le vostre variabili hanno entrambe backslash, risolve anche quello.

Resolve-Path

Resolve-Path vi darà il percorso completo di un luogo. La cosa importante è che espanderà le ricerche con caratteri jolly per voi. Otterrete un array di valori se ci sono più di una corrispondenza.

Resolve-Path -Path 'c:\users\*\documents'Path----C:\users\kevin.marquette\DocumentsC:\users\Public\Documents

Questo enumera tutte le cartelle di documenti degli utenti locali.

Lo uso comunemente su qualsiasi valore di percorso che ottengo come input dell’utente nelle mie funzioni che accettano più file. Lo trovo un modo semplice per aggiungere il supporto per i caratteri jolly ai parametri.

Salvare e leggere i dati

Ora che abbiamo tutti quegli helper CmdLets fuori dai piedi, possiamo parlare delle opzioni che abbiamo per salvare e leggere i dati.

Uso le variabili $Path e $Data per rappresentare il percorso del file e i dati in questi esempi. Lo faccio per mantenere gli esempi più puliti e per riflettere meglio come li useresti in uno script.

Reindirizzamento di base con Out-File

PowerShell è stato introdotto con Out-File come modo per salvare dati su file. Ecco come appare l’aiuto su questo.

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.#>

Per chiunque provenga da file batch, Out-File è il sostituto base dell’operatore di reindirizzamento >. Ecco un esempio di come usarlo.

'This is some text' | Out-File -FilePath $Path

È un comando di base e lo abbiamo da molto tempo. Ecco un secondo esempio che mostra alcune delle limitazioni.

 Get-ChildItem | Select-Object Name, Length, LastWriteTime, Fullname | Out-File -FilePath $Path

Il file risultante si presenta così quando viene eseguito dalla mia cartella temp:

Si può vedere che l’ultima colonna di valori è tagliata. Out-File sta elaborando gli oggetti per la console ma reindirizza l’output su un file. Tutti i problemi che avete per formattare qualcosa nella console si manifesteranno nel vostro file di output. La buona notizia è che abbiamo un sacco di altre opzioni per questo che coprirò di seguito.

Salva dati di testo con Add-Content

Io personalmente non uso Out-File e preferisco usare i comandi Add-Content e Set-Content. C’è anche un comando Get-Content che li accompagna per leggere i dati dei file.

$data | Add-Content -Path $PathGet-Content -Path $Path

Add-Content creerà e appenderà ai file. Set-Content creerà e sovrascriverà i file.

Questi sono buoni comandi multiuso finché le prestazioni non sono un fattore critico nel vostro script. Sono ottimi per richieste individuali o di piccoli contenuti. Per grandi insiemi di dati dove le prestazioni contano più della leggibilità, possiamo rivolgerci al framework .Net. Tornerò su questo.

Importa i dati con Get-Content -Raw

Get-Content è il comando goto per leggere i dati. Per impostazione predefinita, questo comando leggerà ogni riga del file. Si finisce con un array di stringhe. Questo passa bene anche ognuna di esse lungo il tubo.

Il parametro -Raw porterà l’intero contenuto come una stringa multilinea. Questo funziona anche più velocemente perché vengono creati meno oggetti.

Get-Content -Path $Path -Raw

Salva dati basati su colonne con Export-CSV

Se hai bisogno di salvare dati per Excel, Export-CSV è il tuo punto di partenza. Questo è buono per memorizzare un oggetto o dati strutturati di base che possono essere importati in seguito. Il formato CSV è valori separati da virgole in un file di testo. Excel è spesso il visualizzatore predefinito per i file CSV.

Se vuoi importare dati Excel in PowerShell, salvalo come CSV e poi puoi usare Import-CSV. Ci sono altri modi per farlo, ma questo è di gran lunga il più semplice.

$data | Export-CSV -Path $PathImport-CSV -Path $Path

-NoTypeInformation

Export-CSV inserirà informazioni sul tipo nella prima riga del CSV. Se non lo vuoi, allora puoi specificare il parametro -NoTypeInformation.

$data | Export-CSV -Path $Path -NoTypeInformation

Salvare i dati di oggetti ricchi con Export-CliXml

Il comando Export-CliXml è usato per salvare oggetti completi in un file e poi importarli di nuovo con Import-CliXml. Questo è per gli oggetti con valori annidati o tipi di dati complessi. I dati grezzi saranno un oggetto verboso serializzato in XML. La cosa bella è che potete salvare un oggetto nel file e quando lo importate, riavrete quell’oggetto.

Questo formato serializzato non è inteso per essere visualizzato o modificato direttamente. Ecco come appare il file date.clixml:

Non preoccupatevi di cercare di capirlo. Non è previsto che ci scaviate dentro.

Questo è un altro comando che non mi trovo ad usare spesso. Se ho un dataset annidato o gerarchico, allora JSON è il mio modo di salvare quelle informazioni.

Salva dati strutturati con ConvertTo-Json

Quando i miei dati sono annidati e potrei volerli modificare a mano, allora uso ConvertTo-Json per convertirli in JSON. ConvertFrom-Json lo riconverte in un oggetto. Questi comandi non salvano o leggono da soli i file. Dovrete rivolgervi a Get-Content e Set-Content per questo.

C’è una cosa importante da notare in questo esempio. Ho usato un per il mio $Data ma ConvertFrom-Json restituisce invece un . Questo potrebbe non essere un problema ma non c’è una soluzione facile per questo.

Nota anche l’uso del Get-Content -Raw in questo esempio. ConvertFrom-Json si aspetta una stringa per oggetto.

Ecco il contenuto del file JSON di cui sopra:

{ "Address": { "State": "California", "Street": "123 Elm" }}

Nota che questo è simile all’hashtable originale. Questo è il motivo per cui JSON è un formato popolare. È facile da leggere, capire e modificare se necessario. Lo uso sempre per i file di configurazione nei miei progetti.

Altre opzioni e dettagli

Tutti questi CmdLet sono facili da usare. Abbiamo anche altri parametri e l’accesso a .Net per altre opzioni.

Get-Content -ReadCount

Il parametro -ReadCount su Get-Content definisce quante linee che Get-Content leggerà in una volta. Ci sono alcune situazioni in cui questo può migliorare l’overhead di memoria del lavoro con file più grandi.

Questo generalmente include il piping dei risultati a qualcosa che può elaborarli come arrivano e non ha bisogno di mantenere i dati di input.

$dataset = @{}Get-Content -Path $path -ReadCount 15 | Where-Object {$PSItem -match 'error'} | ForEach-Object {$dataset += 1}

Questo esempio conterà quante volte ogni errore si presenta nel $Path. Questa pipeline può processare ogni riga mentre viene letta dal file.

Potreste non avere codice che lo sfrutta spesso, ma questa è una buona opzione da conoscere.

Letture più veloci con System.IO.File

La facilità d’uso che i CmdLet forniscono può avere un piccolo costo in termini di prestazioni. È abbastanza piccolo che non lo noterete per la maggior parte dello scripting che fate. Quando arriverà il giorno in cui avrete bisogno di più velocità, vi troverete a rivolgervi ai comandi nativi .Net. Per fortuna sono facili da usare.

::ReadAllLines( ( Resolve-Path $Path ) )

Questo è proprio come Get-Content -Path $Path in quanto vi ritroverete con una collezione piena di stringhe. Puoi anche leggere i dati come una stringa multilinea.

::ReadAllText( ( Resolve-Path $Path ) )

Il $Path deve essere il percorso completo o cercherà di salvare il file nella tua cartella C:\Windows\System32. Questo è il motivo per cui uso Resolve-Path in questo esempio.

Ecco un esempio di Cmdlet che ho costruito intorno a queste chiamate .Net: Import-Content

Scrive con System.IO.StreamWriter

Sulla stessa nota, possiamo anche usare System.IO.StreamWriter per salvare i dati. Non è sempre più veloce dei Cmdlets nativi. Questo rientra chiaramente nella regola che se le prestazioni contano, testalo.

System.IO.StreamWriter è anche un po’ più complicato dei CmdLet nativi.

try{ $stream = ::new( $Path ) $data | ForEach-Object{ $stream.WriteLine( $_ ) }}finally{ $stream.close()}

Dobbiamo aprire un StreamWriter ad un $path. Poi percorriamo i dati e salviamo ogni linea nel StreamWriter. Una volta finito, chiudiamo il file. Anche questo richiede un percorso completo.

Ho dovuto aggiungere la gestione degli errori per assicurarmi che il file fosse chiuso quando avevamo finito. Potreste voler aggiungere un catch qui dentro per una gestione personalizzata degli errori.

Questo dovrebbe funzionare molto bene per i dati stringa. Se avete problemi, potete chiamare il metodo .ToString() sull’oggetto che state scrivendo nel flusso. Se avete bisogno di più flessibilità, sappiate che avete a disposizione l’intero framework .Net a questo punto.

Salvare XML

Se state lavorando con file XML, potete chiamare il metodo Save() sull’oggetto XML.

$Xml = "<r><data/></r>"$Path = (join-path $pwd 'File.xml')$Xml.Save($Path)

Proprio come gli altri metodi .Net in System.IO, è necessario specificare il percorso completo del file. Io uso $pwd in questo esempio perché è una variabile automatica che contiene il risultato di Get-Location (percorso locale).

Nota veloce sulla codifica

La codifica del file è il modo in cui i dati vengono trasformati in binario quando vengono salvati su disco. La maggior parte delle volte funziona, a meno che tu non faccia molto lavoro su più piattaforme.

Se hai problemi con la codifica, la maggior parte dei CmdLet supporta la specificazione della codifica. Se vuoi impostare la codifica di default per ogni comando, puoi usare l’hashtable $PSDefaultParameterValues in questo modo:

Puoi trovare maggiori informazioni su come usare PSDefaultParameterValues nel mio post sulle Hashtables.

Concludendo

Lavorare con i file è un compito così comune che dovresti dedicare del tempo a conoscere queste opzioni. Speriamo che abbiate visto qualcosa di nuovo e che possiate metterlo in pratica nei vostri script.

Queste sono due funzioni che implementano le idee di cui abbiamo parlato

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *