powershell obtenir le nombre de lignes de gros (gros) fichier

l'Un des moyens de ne. de lignes d'un fichier est de cette méthode dans powershell

PS C:UsersPranavDesktopPS_Test_Scripts> $a=Get-Content .sub.ps1
PS C:UsersPranavDesktopPS_Test_Scripts> $a.count
34
PS C:UsersPranavDesktopPS_Test_Scripts> 

cependant, quand j'ai un gros fichier texte de 800 Mo, Comment puis-je obtenir le numéro de ligne sans lire tout le fichier ?

la méthode ci-dessus consomme trop de mémoire vive, ce qui provoque un crash du script ou prend trop de temps à terminer.

16
demandé sur Pranav 2012-08-23 08:13:53

6 réponses

Utiliser Get-Content -Read $nLinesAtTime pour lire votre fichier, partie par partie

$nlines = 0; 
#read file by 1000 lines at a time
gc $YOURFILE -read 1000 | % { $nlines += $_.Length }; 
[string]::Format("{0} has {1} lines", $YOURFILE, $nlines)

Et ici est simple, mais lente script pour valider les travaux sur les petites fichier

gc $YOURFILE | Measure-Object -Line
18
répondu Akim 2012-08-23 04:50:03

voici un script Powershell que j'ai concocté et qui montre quelques méthodes différentes de comptage de lignes dans un fichier texte, ainsi que le temps et la mémoire nécessaires pour chaque méthode. Les résultats (ci-dessous) montrent des différences claires dans le temps et de mémoire. Pour mes tests, il semble que le bon endroit était Get-Content, en utilisant un réglage ReadCount de 100. Les autres tests nécessitaient beaucoup plus de temps et / ou de mémoire.

#$testFile = 'C:\test_small.csv' # 245 lines, 150 KB
#$testFile = 'C:\test_medium.csv' # 95,365 lines, 104 MB
$testFile = 'C:\test_large.csv' # 285,776 lines, 308 MB

# Using ArrayList just because they are faster than Powershell arrays, for some operations with large arrays.
$results = New-Object System.Collections.ArrayList

function AddResult {
param( [string] $sMethod, [string] $iCount )
    $result = New-Object -TypeName PSObject -Property @{
        "Method" = $sMethod
        "Count" = $iCount
        "Elapsed Time" = ((Get-Date) - $dtStart)
        "Memory Total" = [System.Math]::Round((GetMemoryUsage)/1mb, 1)
        "Memory Delta" = [System.Math]::Round(((GetMemoryUsage) - $dMemStart)/1mb, 1)
    }
    [void]$results.Add($result)
    Write-Output "$sMethod : $count"
    [System.GC]::Collect()
}

function GetMemoryUsage {
    # return ((Get-Process -Id $pid).PrivateMemorySize)
    return ([System.GC]::GetTotalMemory($false))
}

# Get-Content -ReadCount 1
[System.GC]::Collect()
$dMemStart = GetMemoryUsage
$dtStart = Get-Date
$count = 0
Get-Content -Path $testFile -ReadCount 1 |% { $count++ }
AddResult "Get-Content -ReadCount 1" $count

# Get-Content -ReadCount 10,100,1000,0
# Note: ReadCount = 1 returns a string.  Any other value returns an array of strings.
# Thus, the Count property only applies when ReadCount is not 1.
@(10,100,1000,0) |% {
    $dMemStart = GetMemoryUsage
    $dtStart = Get-Date
    $count = 0
    Get-Content -Path $testFile -ReadCount $_ |% { $count += $_.Count }
    AddResult "Get-Content -ReadCount $_" $count
}

# Get-Content | Measure-Object
$dMemStart = GetMemoryUsage
$dtStart = Get-Date
$count = (Get-Content -Path $testFile -ReadCount 1 | Measure-Object -line).Lines
AddResult "Get-Content -ReadCount 1 | Measure-Object" $count

# Get-Content.Count
$dMemStart = GetMemoryUsage
$dtStart = Get-Date
$count = (Get-Content -Path $testFile -ReadCount 1).Count
AddResult "Get-Content.Count" $count

# StreamReader.ReadLine
$dMemStart = GetMemoryUsage
$dtStart = Get-Date
$count = 0
# Use this constructor to avoid file access errors, like Get-Content does.
$stream = New-Object -TypeName System.IO.FileStream(
    $testFile,
    [System.IO.FileMode]::Open,
    [System.IO.FileAccess]::Read,
    [System.IO.FileShare]::ReadWrite)
if ($stream) {
    $reader = New-Object IO.StreamReader $stream
    if ($reader) {
        while(-not ($reader.EndOfStream)) { [void]$reader.ReadLine(); $count++ }
        $reader.Close()
    }
    $stream.Close()
}

AddResult "StreamReader.ReadLine" $count

$results | Select Method, Count, "Elapsed Time", "Memory Total", "Memory Delta" | ft -auto | Write-Output

Voici les résultats pour le fichier texte contenant ~95 k lignes, 104 MO:

Method                                    Count Elapsed Time     Memory Total Memory Delta
------                                    ----- ------------     ------------ ------------
Get-Content -ReadCount 1                  95365 00:00:11.1451841         45.8          0.2
Get-Content -ReadCount 10                 95365 00:00:02.9015023         47.3          1.7
Get-Content -ReadCount 100                95365 00:00:01.4522507         59.9         14.3
Get-Content -ReadCount 1000               95365 00:00:01.1539634         75.4         29.7
Get-Content -ReadCount 0                  95365 00:00:01.3888746          346        300.4
Get-Content -ReadCount 1 | Measure-Object 95365 00:00:08.6867159         46.2          0.6
Get-Content.Count                         95365 00:00:03.0574433        465.8        420.1
StreamReader.ReadLine                     95365 00:00:02.5740262         46.2          0.6

Voici les résultats pour un fichier plus grand (contenant ~285k lignes, 308 Mo):

Method                                    Count  Elapsed Time     Memory Total Memory Delta
------                                    -----  ------------     ------------ ------------
Get-Content -ReadCount 1                  285776 00:00:36.2280995         46.3          0.8
Get-Content -ReadCount 10                 285776 00:00:06.3486006         46.3          0.7
Get-Content -ReadCount 100                285776 00:00:03.1590055         55.1          9.5
Get-Content -ReadCount 1000               285776 00:00:02.8381262         88.1         42.4
Get-Content -ReadCount 0                  285776 00:00:29.4240734        894.5        848.8
Get-Content -ReadCount 1 | Measure-Object 285776 00:00:32.7905971         46.5          0.9
Get-Content.Count                         285776 00:00:28.4504388       1219.8       1174.2
StreamReader.ReadLine                     285776 00:00:20.4495721           46          0.4
15
répondu Pseudothink 2015-12-13 20:21:07

la première chose à essayer est de streamer Get-Content et de construire la ligne de comptage un à la fois, plutôt que de stocker toutes les lignes dans un tableau à la fois. Je pense que cela donnera un comportement de streaming approprié - c.-à-d. le fichier entier ne sera pas en mémoire à la fois, juste la ligne courante.

$lines = 0
Get-Content .\File.txt |%{ $lines++ }

et comme le suggère l'autre réponse, ajouter -ReadCount pourrait accélérer les choses.

Si cela ne fonctionne pas pour vous (trop lent ou trop de mémoire), vous pouvez aller directement à un StreamReader:

$count = 0
$reader = New-Object IO.StreamReader 'c:\logs\MyLog.txt'
while($reader.ReadLine() -ne $null){ $count++ }
$reader.Close()  # don't forget to do this.  Ideally put this in a try/finally block to make sure it happens
10
répondu latkin 2012-08-23 05:04:56

Voici une doublure basée sur le post de Pseudothink.

Lignes dans un fichier:

"the_name_of_your_file.txt" |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"}

Tous les fichiers du répertoire courant (individuellement):

Get-ChildItem "." |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"}

Explication:

"the_name_of_your_file.txt" - > ne fait rien, fournit juste le nom du fichier pour les prochaines étapes, doit être double Cité

|% -> alias ForEach-Object, itère sur les éléments fournis (un seul dans ce cas), accepte le contenu pipé comme une entrée, l'élément courant est sauvegardé à $_

$n = $_ -> $n que le nom du fichier est sauvegardé pour plus tard à partir de $_,en fait, cela peut ne pas être nécessaire

$c = 0 - > initialisation de $c count

Get-Content -Path $_ -ReadCount 1000 > lire 1000 lignes à partir d'un fichier fourni (voir les autres réponses du sujet)

|% -> foreach faire ajouter des numéros de lignes réellement lire $c (comme 1000 + 1000 + 123)

"$n; $c" - > une fois le fichier de lecture terminé, imprimer nom de fichier; nombre de lignes

Get-ChildItem "." -> ajoute simplement plus d'éléments dans le tuyau que le nom de fichier

8
répondu Honza 2018-07-02 11:50:05

voici quelque chose que j'ai écrit pour essayer de réduire l'utilisation de la mémoire lors de l'analyse de l'espace blanc dans mon fichier txt. Cela dit,l'utilisation de la mémoire toujours obtenir sorte de élevé, mais le processus prend moins de temps à s'exécuter. Juste pour vous donner un peu de fond de mon fichier, le fichier avait plus de 2 millions de dossiers et d'espace blanc à l'avant et à l'arrière de chaque ligne. Je crois que le temps total était de 5 + minutes Veuillez me faire part de vos commentaires s'il y a un moyen d'améliorer le formatage. grâce

   $testing = 'C:\Users\something\something\test3.txt'

 $filecleanup =  gci $testing

  foreach ($file in $filecleanup )
  { $file1 = gc $file -readcount 1000 |foreach{ $_.Trim()} 
  $file1 > $filecleanup}
1
répondu user2176024 2015-08-22 14:59:38

Voici une solution qui utilise .NET:

[Linq.Enumerable]::Count([System.IO.File]::ReadLines("FileToCount.txt"))

ce n'est pas très interruptible, mais c'est très facile sur la mémoire.

0
répondu greenjaed 2018-10-02 20:55:50