IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Shell et commandes GNU Discussion :

Rechercher / remplacer rapidement du texte


Sujet :

Shell et commandes GNU

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2009
    Messages
    171
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2009
    Messages : 171
    Par défaut Rechercher / remplacer rapidement du texte
    Bonjour à tous,

    Je dois souvent mettre à jour une date qui se trouve au début de nombreux fichiers Xml très volumineux.

    Actuellement j'utilise la commande suivante qui fonctionne très bien :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sed -i "s/<day>20160202<\/day>/<day>20160203<\/day>/g" ./File-*
    Cependant, vu la taille et le nombre de mes fichiers, cela peut parfois me prendre un temps considérable (1 minute par fichier de 1.5GB, jusqu'à 8-10 minutes pour des fichiers de 20GB).

    Sachant que ce texte recherché se trouve uniquement en en-tête du fichier (dans les 10 premières lignes), est-ce qu'il y a une commande qui me permette de gagner du temps ?

    Avec la commande que j'utilise, il recopie tout le contenu du fichier dans un fichier temporaire et cette étape me fait perdre énormément de temps.

    Merci d'avance pour votre aide !

  2. #2
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 368
    Par défaut
    Bonjour,

    Il n'y a pas trop de solution car tous les outils sont un peu obliger de passer par des fichiers tampon.
    Après, j'aurai du mal à te plaindre: Avoir des fichiers xml de 20 Go, pour moi, c'est une aberration.

    Sinon, tu peux éventuellement réduire ton temps globale en découpant ton simple sed -i '...' file-* en plusieurs process.
    Autant de process que tu as de cpu sans dépasser disons les 15 process en // car de toute façon, tu seras limité par des attentes I/O.

  3. #3
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    salut,

    comme dit disedorgue y'a assez peu de possibilités, j'ai déjà vu faire à coups de dd mais je ne me souviens plus de comment c'était gaulé pour être honnête

    sinon peut-être via ed avec un truc du genre (ça marche aussi avec vim et cie.) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ed -s fichier <<EOF
    ,s/<day>20160202<\/day>/<day>20160203<\/day>/g
    wq
    EOF
    le seul inconvénient c'est qu'inévitablement ça va pomper toute la mémoire pour ouvrir/lire le fichier...

    Edit: un truc comme ça devrait fonctionner, notamment parceque dans ton cas la chaine à remplacer à la même taille que la chaine de remplacement (en fait y'a guère qu'un chiffre qui change) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    str_from='<day>20160202</day>'
    str_to='<day>20160203</day>'
    grep -b -o -F "${str_from}" fichier | cut -d':' -f1 | while read offset; do echo -ne "${str_to}" | dd of=fichier conv=notrunc bs=1 seek=${offset} 2>/dev/null; done
    à voir si ça accroit vraiment les perfs ou si ça génère plus d'io que ce que ça aide...

  4. #4
    Modérateur
    Avatar de jlliagre
    Homme Profil pro
    Ingénieur support avancé & développement
    Inscrit en
    Juin 2007
    Messages
    2 695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur support avancé & développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 695
    Par défaut
    La taille de la chaîne de remplacement étant égale à la taille de la chaîne à remplacer, un fichier intermédiaire est techniquement superflu. Malheureusement, les utilitaires standard vont lire la totalité du fichier puis en le réécrire aussi en totalité en passant par ce fichier intermédiaire ou par son image en mémoire.

    Lire puis écrire tout le fichier est extrêmement pénalisant puisque seuls quelques secteurs du disque sont concernés dans ton cas.

    Voici une méthode avec `dd` qui fonctionne si la chaîne à remplacer se trouve dans les 4096 premiers octets du fichier (à augmenter donc si ce n'est pas le cas):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    dd if=File-001.xml of=tmp.xml bs=4096 count=1
    sed -i "s/<day>20160202<\/day>/<day>20160203<\/day>/g" tmp.xml 
    dd if=tmp.xml of=File-001.xml seek=0 conv=notrunc

  5. #5
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 368
    Par défaut
    Pas mal la solution avec "dd" , ça fonctionne aussi sur un partage nfs ou samba ?

  6. #6
    Modérateur
    Avatar de jlliagre
    Homme Profil pro
    Ingénieur support avancé & développement
    Inscrit en
    Juin 2007
    Messages
    2 695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur support avancé & développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 695
    Par défaut
    Sûrement, sinon ce serait un gros bug de l'implémentation de NFS ou CIFS ...

  7. #7
    Membre confirmé
    Homme Profil pro
    Développeur
    Inscrit en
    Juin 2009
    Messages
    171
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2009
    Messages : 171
    Par défaut
    Merci à tous pour votre aide et vos idées

    Citation Envoyé par jlliagre Voir le message
    La taille de la chaîne de remplacement étant égale à la taille de la chaîne à remplacer, un fichier intermédiaire est techniquement superflu. Malheureusement, les utilitaires standard vont lire la totalité du fichier puis en le réécrire aussi en totalité en passant par ce fichier intermédiaire ou par son image en mémoire.

    Lire puis écrire tout le fichier est extrêmement pénalisant puisque seuls quelques secteurs du disque sont concernés dans ton cas.

    Voici une méthode avec `dd` qui fonctionne si la chaîne à remplacer se trouve dans les 4096 premiers octets du fichier (à augmenter donc si ce n'est pas le cas):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    dd if=File-001.xml of=tmp.xml bs=4096 count=1
    sed -i "s/<day>20160202<\/day>/<day>20160203<\/day>/g" tmp.xml 
    dd if=tmp.xml of=File-001.xml seek=0 conv=notrunc
    ==> ca marche niquel, même pas le temps de cliquer que mon fichier est déjà à jour !


    Citation Envoyé par Flodelarab Voir le message
    Bonjour

    Si l'entête prend les 20 premières lignes, est-ce que ceci va plus vite ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sed -i "1,20s/<day>20160202<\/day>/<day>20160203<\/day>/g" ./File-*
    Celui lui évitera de chercher une correspondance dans le million de lignes qui suit.
    ==> Ca semble allez un peu plus vite effectivement, genre 40secondes au lieu de 1 minute (même si j'ai testé très vite)


    Citation Envoyé par disedorgue Voir le message
    Bonjour,

    Il n'y a pas trop de solution car tous les outils sont un peu obliger de passer par des fichiers tampon.
    Après, j'aurai du mal à te plaindre: Avoir des fichiers xml de 20 Go, pour moi, c'est une aberration.

    Sinon, tu peux éventuellement réduire ton temps globale en découpant ton simple sed -i '...' file-* en plusieurs process.
    Autant de process que tu as de cpu sans dépasser disons les 15 process en // car de toute façon, tu seras limité par des attentes I/O.
    ==> Pas ma faute pour les volumes des fichiers xml, on envoi des données dans une application de gouvernance et le transfert se fait en XML. Actuellement nous n'avons pas d'autre alternative.

    Je dois encore tester la solution de BufferBob


    Merci à tous !

  8. #8
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    Citation Envoyé par Bouga74 Voir le message
    Je dois encore tester la solution de BufferBob
    la solution que j'ai proposé à base de dd est intéressante dans le sens où elle est "tout automatique", mais elle a comme inconvénient de faire autant de dd successifs et donc autant d'ouvertures/fermetures du fichier qu'il y a d'occurrences recherchées

    si tu sais avec certitude identifier une plage d'octets dans le fichier pour réduire le traitement la solution de jilliagre ou de zipe31 (ça revient au même modulo le oneliner) est à mon avis la plus intéressante

    pour l’anecdote ma solution repose sur grep et les options -b, -o et -F
    • -b demande à grep d'afficher l'offset dans le fichier de la correspondance trouvée, juste ce dont à besoin dd
    • -o permet de ne rater aucune occurrence (s'il y en a plusieurs sur la même ligne)
    • -F permet une recherche en fixed string, option souvent oubliée, mais qui dans un fichier de 20G fait probablement la différence avec le traitement par défaut de grep qui cherche à matcher une expression régulière (fût-elle basique)


    Citation Envoyé par Flodelarab Voir le message
    Si on en est là, ça vaut le coup de faire un patch en C++ qui ne modifiera qu'une séquence d'octets préalablement repérée. Non ?
    ouai, je suis assez d'accord, à ce stade j'aurai déjà sorti Perl ou C

  9. #9
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2011
    Messages
    1 946
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Janvier 2011
    Messages : 1 946
    Par défaut
    Salut,

    Citation Envoyé par jlliagre Voir le message
    Voici une méthode avec `dd` qui fonctionne si la chaîne à remplacer se trouve dans les 4096 premiers octets du fichier (à augmenter donc si ce n'est pas le cas):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    dd if=File-001.xml of=tmp.xml bs=4096 count=1
    sed -i "s/<day>20160202<\/day>/<day>20160203<\/day>/g" tmp.xml 
    dd if=tmp.xml of=File-001.xml seek=0 conv=notrunc
    dd if=File-001.xml bs=4096 count=1 | sed 's/<day>20160202<\/day>/<day>20160203<\/day>/' | dd of=File-001.xml seek=0 conv=notrunc devrait le faire aussi, non ?

  10. #10
    Modérateur
    Avatar de jlliagre
    Homme Profil pro
    Ingénieur support avancé & développement
    Inscrit en
    Juin 2007
    Messages
    2 695
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur support avancé & développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 2 695
    Par défaut
    Citation Envoyé par zipe31 Voir le message
    dd if=File-001.xml bs=4096 count=1 | sed 's/<day>20160202<\/day>/<day>20160203<\/day>/' | dd of=File-001.xml seek=0 conv=notrunc devrait le faire aussi, non ?
    Oui ! On gagne quelque microsecondes ;-)

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Rechercher/remplacer, supprimer du texte
    Par Vinceoreste dans le forum Excel
    Réponses: 6
    Dernier message: 03/05/2018, 14h38
  2. Recherche/Remplacement dans un texte
    Par John Fullspeed dans le forum Codes sources à télécharger
    Réponses: 0
    Dernier message: 09/02/2013, 12h00
  3. Réponses: 8
    Dernier message: 12/11/2007, 10h16
  4. [RegEx] Remplacement rapide dans un fichier texte (RTF)
    Par johweb dans le forum Langage
    Réponses: 12
    Dernier message: 17/01/2007, 09h04
  5. Réponses: 4
    Dernier message: 12/10/2006, 17h03

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo