Leggi il file riga per riga in PowerShell


103

Voglio leggere un file riga per riga in PowerShell. In particolare, voglio scorrere il file, memorizzare ogni riga in una variabile nel ciclo ed eseguire alcune elaborazioni sulla riga.

Conosco l'equivalente di Bash:

while read line do
    if [[ $line =~ $regex ]]; then
          # work here
    fi
done < file.txt

Non c'è molta documentazione sui loop di PowerShell.


La risposta selezionata da Mathias non è un'ottima soluzione. Get-Contentcarica l'intero file in memoria in una volta, cosa che non funzionerà o si bloccherà su file di grandi dimensioni.
Kolob Canyon

1
@KolobCanyon che è completamente falso. Per impostazione predefinita, Get-Content carica ogni riga come un oggetto nella pipeline. Se stai collegando a una funzione che non specifica un processblocco e sputa un altro oggetto per riga nella pipeline, allora quella funzione è il problema. Eventuali problemi con il caricamento dell'intero contenuto in memoria non sono imputabili a Get-Content.
The Fish

@TheFish foreach($line in Get-Content .\file.txt)Caricherà l'intero file in memoria prima che inizi l'iterazione. Se non mi credi, prendi un file di registro da 1 GB e provalo.
Kolob Canyon

2
@KolobCanyon Non è quello che hai detto. Hai detto che Get-Content carica tutto in memoria, il che non è vero. Il tuo esempio modificato di foreach sarebbe, sì; foreach non è a conoscenza della pipeline. Get-Content .\file.txt | ForEach-Object -Process {}è a conoscenza della pipeline e non caricherà l'intero file in memoria. Per impostazione predefinita, Get-Content passerà una riga alla volta attraverso la pipeline.
The Fish

Risposte:


180

Non c'è molta documentazione sui loop di PowerShell.

La documentazione sul loop in PowerShell è abbondante, e si potrebbe voler controllare i seguenti argomenti della Guida: about_For, about_ForEach, about_Do, about_While.

foreach($line in Get-Content .\file.txt) {
    if($line -match $regex){
        # Work here
    }
}

Un'altra soluzione idiomatica di PowerShell al tuo problema è reindirizzare le righe del file di testo al ForEach-Objectcmdlet :

Get-Content .\file.txt | ForEach-Object {
    if($_ -match $regex){
        # Work here
    }
}

Invece di eseguire la corrispondenza regex all'interno del ciclo, puoi collegare le linee Where-Objectper filtrare solo quelle che ti interessano:

Get-Content .\file.txt | Where-Object {$_ -match $regex} | ForEach-Object {
    # Work here
}

I collegamenti non sono interrotti, ma ora vengono reindirizzati a docs.microsoft.com.
Peter Mortensen

@KolobCanyon che non è mai stato menzionato come un problema nell'OP.
The Fish

53

Get-Contentha una cattiva prestazione; cerca di leggere il file in memoria tutto in una volta.

Il lettore di file C # (.NET) legge ogni riga una alla volta

Miglior Performace

foreach($line in [System.IO.File]::ReadLines("C:\path\to\file.txt"))
{
       $line
}

O leggermente meno performante

[System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object {
       $_
}

L' foreachaffermazione sarà probabilmente leggermente più veloce di ForEach-Object(vedere i commenti di seguito per ulteriori informazioni).


5
Probabilmente userei [System.IO.File]::ReadLines("C:\path\to\file.txt") | ForEach-Object { ... }. L' foreachistruzione caricherà l'intera raccolta su un oggetto . ForEach-Objectutilizza una pipeline con cui eseguire lo streaming. Ora l' foreachistruzione sarà probabilmente leggermente più veloce del ForEach-Objectcomando, ma questo perché il caricamento dell'intera cosa in memoria di solito è più veloce. Get-Contentè comunque terribile.
Bacon Bits

@BaconBits foreach()è un alias diForeach-Object
Kolob Canyon

16
Questo è un malinteso molto comune. foreachè una dichiarazione, come if, foro while. ForEach-Objectè un comando, come Get-ChildItem. Esiste anche un alias predefinito di foreachfor ForEach-Object, ma viene utilizzato solo quando è presente una pipeline. Vedere la lunga spiegazione in Get-Help about_Foreacho fare clic sul collegamento nel mio commento precedente che rimanda a un intero articolo di The Scripting Guys di Microsoft sulle differenze tra l'istruzione e il comando.
Bacon Bits

4
@BaconBits blogs.technet.microsoft.com/heyscriptingguy/2014/07/08/… Ho imparato qualcosa di nuovo. Grazie. Ho pensato che fossero gli stessi perché Get-Alias foreach=> Foreach-Object, ma hai ragione, ci sono differenze
Kolob Canyon

2
Funzionerà, ma ti consigliamo di passare $lineal $_blocco di script del ciclo.
Bacon Bits

1

L'interruttore onnipotente funziona bene qui:

'one
two
three' > file

$regex = '^t'

switch -regex -file file { 
  $regex { "line is $_" } 
}

Produzione:

line is two
line is three
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.