Remplacez le texte rapidement dans un fichier très volumineux

25

J'ai un fichier texte de 25 Go qui a besoin d'une chaîne remplacée sur seulement quelques lignes. Je peux l'utiliser sedavec succès, mais cela prend beaucoup de temps pour fonctionner.

sed -i 's|old text|new text|g' gigantic_file.sql

Existe-t-il un moyen plus rapide de procéder?

eisaacson
la source
Connaissez-vous les numéros de ligne où se trouve le texte à remplacer? Si ce n'est pas votre seule option pour l'accélérer, c'est d'obtenir un ordinateur plus rapide. Le fait que vous ayez une grande quantité de données signifie qu'il faudra beaucoup de temps pour les parcourir.
David King
Je peux chercher les numéros de ligne assez rapidement, alors oui.
eisaacson
Vous pouvez également utiliser plusieurs cœurs de processeur pour l'accélérer - rankfocus.com/use-cpu-cores-linux-commands
ahaswer
N'utilisez pas sed pour les gros fichiers. Jetez plutôt un œil à vi ou vim .
MikeJRamsey56

Réponses:

26

Tu peux essayer:

sed -i '/old text/ s//new text/g' gigantic_file.sql

De cette référence :

OPTIMISATION DE LA VITESSE: Si la vitesse d'exécution doit être augmentée (en raison de fichiers d'entrée volumineux ou de processeurs lents ou de disques durs), la substitution sera exécutée plus rapidement si l'expression "find" est spécifiée avant de donner le "s /.../. ../" instruction.

Voici une comparaison sur un fichier 10G. Avant:

$ time sed -i 's/original/ketan/g' wiki10gb
real    5m14.823s
user    1m42.732s
sys     1m51.123s

Après:

$ time sed -i '/ketan/ s//original/g' wiki10gb
real    4m33.141s
user    1m20.940s
sys     1m44.451s
mkc
la source
Le dernier sedest mal orthographié. J'ai édité ce post hier pour corriger la dernière sedcommande qui devrait être time sed -i '/original/ s//ketan/g' wiki10gbet non time sed -i '/ketan/ s//original/g' wiki10gb. Je reviens à mon montage aujourd'hui parce que 1. les temps ne correspondent plus à la commande et 2. J'ai fait le même test avec GNU sed sur un fichier de 3 Go et je n'observe aucune différence entre les deux sedalternatives. Je soupçonne que la différence de temps est due à une faute d'orthographe.
xhienne
@xhienne Je ne sais pas trop ce que vous entendez par faute d'orthographe. Dans la première manche, je remplace le mot «original» par «ketan» et dans le second, je remplace le terme «ketan» par le terme «original», ce qui entraîne un nombre égal de substitutions dans les deux cas.
mkc
1
J'appliquais un "correctif" signalé par un nouvel utilisateur avec une réputation insuffisante. Maintenant je comprends ce que tu as fait. Cependant, si vous voulez prouver qu'une syntaxe est meilleure que l'autre, vous devez faire exactement la même opération, ce qui n'est pas le cas ici (au niveau du processeur, la recherche d'une chaîne de 5 caractères n'est pas la même chose que la recherche d'un Chaîne de 7 caractères). De plus, ce type de test sur un fichier de 10 Go dépend fortement de la charge de votre machine (CPU, disque). J'ai vu personnellement beaucoup de fluctuations dans les timerésultats, mais dans l'ensemble, il n'y avait pas de différence de temps.
xhienne
Je crois que cela est lié - voir la réponse acceptée ici, stackoverflow.com/questions/11145270/… >> sed diffuse l'intégralité du fichier, mais comme indiqué dans cette réponse, spécifier le numéro de ligne (s'il est connu) aide: dans mon cas , une augmentation ~ 2 fois de la vitesse d'exécution (GNU sed 4.5). Vous pouvez grep -n ou ripgrep (rg) pour trouver des numéros de ligne, basés sur des recherches de modèles. En effet, spécifier le numéro de ligne, c'est comme avoir un résultat de recherche sur ce fichier, selon la réponse ci-dessus.
Victoria Stuart
1

La réponse courte est "Non" - votre facteur limitant sur ce type d'opération est le disque IO. Il n'y a aucun moyen de diffuser plus rapidement 25 Go de disque. Vous pourriez obtenir une amélioration mineure si vous n'effectuez pas de modification sur place et que vous écrivez le résultat de lased sur un lecteur séparé (si vous en avez un de disponible) - parce que de cette façon, vous pouvez lire à partir de l'un, tout en écrivant sur un autre et il y a légèrement moins de conflits en conséquence.

Vous pourrez peut- être l'accélérer un peu en n'utilisant pas le moteur d'expression régulière pour chaque ligne - donc par exemple en utilisant perl (je suis sûr que vous pouvez le faire avec sedmais je ne connais pas la syntaxe) - cela commencera à partir de ligne 10 000 et suivantes.

perl -pe '$. > 10_000 && s/old_text/new_text/g' 

Et s'il y a des complications dans les RE (métacaractères), leur minimisation améliorera légèrement l'efficacité du moteur d'expression régulière.

Sobrique
la source
1
Dans sed ce seraitsed -i '10000,$ s/old_text/new_text/g'
Dani_l
Charmant. Je ne sais pas comment se sedcompare - je suppose légèrement plus rapide, mais pas beaucoup à cause de la taille du fichier.
Sobrique
Je suppose que perl est plus rapide que sed, mais sed est un peu moins cryptique, ou nécessite plutôt moins de courbe d'apprentissage initiale.
Dani_l
1
Voir, maintenant , je l' aurais dit le contraire - vous pouvez (presque) écrire seddans perl, mais celui - ci vous permet également d' écrire des scripts plus bavard aussi.
Sobrique
0

Si les nouveaux et les anciens textes ont la même longueur, vous pouvez rechercher dans le fichier et écrire uniquement les octets modifiés, au lieu de copier tout le fichier. Sinon, vous êtes coincé dans le déplacement de nombreuses données.

Remarque: ceci est délicat et implique l'écriture de code personnalisé.

Consultez la page de manuel de fseek si vous travaillez en C ou C ++, ou vos wrappers de langage préférés pour les appels système de recherche et d'écriture.

Si vous insistez sur l'utilisation de la ligne de commande uniquement et que vous pouvez obtenir les décalages d'octets du texte, vous pouvez écrire le texte de remplacement en place avec des commandes "dd" soigneusement écrites.

stolenmoment
la source