Je travaille avec des fichiers texte de plusieurs gigaoctets et je souhaite effectuer un traitement de flux sur eux à l'aide de PowerShell. C'est des choses simples, il suffit d'analyser chaque ligne et d'extraire des données, puis de les stocker dans une base de données.
Malheureusement, get-content | %{ whatever($_) }
semble garder en mémoire l'ensemble des lignes à ce stade du tuyau. C'est aussi étonnamment lent, prenant beaucoup de temps pour tout lire.
Ma question est donc en deux parties:
- Comment puis-je faire en sorte qu'il traite le flux ligne par ligne et ne garde pas le tout en mémoire tampon? Je voudrais éviter d'utiliser plusieurs Go de RAM à cette fin.
- Comment puis-je le faire fonctionner plus rapidement? L'itération de PowerShell sur un
get-content
semble être 100 fois plus lente qu'un script C #.
J'espère qu'il y a quelque chose de stupide que je fais ici, comme manquer un -LineBufferSize
paramètre ou quelque chose ...
powershell
stream
Scobi
la source
la source
get-content
, définissez -ReadCount sur 512. Notez qu'à ce stade, $ _ dans Foreach sera un tableau de chaînes.Get-Content
à une variable car cela chargera le fichier entier en mémoire. Par défaut, dans un pipleline,Get-Content
traite le fichier une ligne à la fois. Tant que vous n'accumulez pas les résultats ou n'utilisez pas une applet de commande qui s'accumule en interne (comme Sort-Object et Group-Object), le coup de mémoire ne devrait pas être trop mauvais. Foreach-Object (%) est un moyen sûr de traiter chaque ligne, une à la fois.get-content | % -End { }
cela se plaint parce que vous n'avez pas fourni de bloc de processus. Il ne peut donc pas utiliser -End par défaut, il doit utiliser -Process par défaut. Et essayez de1..5 | % -process { } -end { 'q' }
voir que le bloc de fin ne se produit qu'une seule fois, l'habituelgc | % { $_ }
ne fonctionnerait pas si le scriptblock était par défaut -End ...Réponses:
Si vous êtes vraiment sur le point de travailler sur des fichiers texte de plusieurs gigaoctets, n'utilisez pas PowerShell. Même si vous trouvez un moyen de le lire plus rapidement, le traitement d'un grand nombre de lignes sera de toute façon lent dans PowerShell et vous ne pouvez pas éviter cela. Même les boucles simples coûtent cher, disons pour 10 millions d'itérations (bien réelles dans votre cas), nous avons:
MISE À JOUR: Si vous n'avez toujours pas peur, essayez d'utiliser le lecteur .NET:
MISE À JOUR 2
Il y a des commentaires sur un code éventuellement meilleur / plus court. Il n'y a rien de mal avec le code d'origine avec
for
et ce n'est pas un pseudo-code. Mais la variante la plus courte (la plus courte?) De la boucle de lecture estla source
do { $line = $reader.ReadLine(); $line } while ($line -neq $null)
for ( $line = $reader.ReadLine(); $line -ne $null; $line = $reader.ReadLine() ) { $line }
while($null -ne ($line = $read.ReadLine())) {$line}
. Mais le sujet ne concerne pas vraiment de telles choses.System.IO.File.ReadLines()
est parfait pour ce scénario. Il renvoie toutes les lignes d'un fichier, mais vous permet de commencer immédiatement à itérer sur les lignes, ce qui signifie qu'il n'a pas à stocker tout le contenu en mémoire.Nécessite .NET 4.0 ou supérieur.
http://msdn.microsoft.com/en-us/library/dd383503.aspx
la source
Si vous souhaitez utiliser PowerShell directement, consultez le code ci-dessous.
la source
Get-Content
est très lent sur les gros fichiers.