Parcours de fichiers avec sed

s€b -  
 s€b -
Bonjour,
Je suis en train de développer un script pour parcourir une liste de fichiers, et pour chacun regarder le 8ième champ délimité par des ';'. Sur ce champ-là, je vérifie que c'est une date correcte, sinon j'inscris un message dans une log. Voici pour info le script initial, qui fonctionne bien mais qui est trop long à s'exécuter (30mn pour 200 Mo, alors que j'ai plusieurs Go à parcourir) :

#répertoire contenant les fichiers de contact
REP=/mon_repertoire

#fichier de log
log=/mon_repertoire_log/controle_date.log

# debut du traitement de controle de la date de contact
echo 'debut du traitement'

for file in `find $REP -type f`
do
echo $file
compteur=0
erreurs=0
while read ligne
do
compteur=`expr $compteur + 1`
dateTestee=`echo $ligne | cut -d';' -f8`
if [ -n "$ligne" ]
then
echo $dateTestee | grep -q "[0-3][0-9]/[0-1][0-9]/[0-9]\{4\}"
if [ $? -eq 0 ]
then
echo 'date bonne : '${file}' ligne : '$compteur >> /dev/null
else
echo 'date mauvaise : '${file} >> $log
erreurs=`expr $erreurs + 1`
if [ $erreurs -gt 100 ]
then
echo 'arret traitement '$file
break
fi
fi
fi
done < $file
echo 'fin du fichier '$file
done

echo "fin du traitement avec succes"

# fin de la boucle pour balayer le r?rtoire

J'ai donc essayé de l'améliorer avec sed, mais je bloque :
for file in `find $REP -type f`
do
echo $file
compteur=0
erreurs=0
sed 's/^[^;]*;[^;]*;[^;]*;[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\).*/\1/; /[0-3][0-9\/[0-1][0-9]\/[0-9]\{4\}/d' $file

echo 'fin du fichier '$file
done

echo "fin du traitement avec succes"

# fin de la boucle pour balayer le r?rtoire

ce qui devrait en théorie prendre le 8ième champ (première expression régulière) puis contrôler que c'est une date et le supprimer de l'affichage. Mais cela m'affiche toutes les lignes et n'en supprime aucune :-(

Est-ce qu'un expert shell/sed/expression régulière au rait une idée ?

Merci !
Configuration: Windows XP
Firefox 3.5.5

17 réponses

  1. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
     
    Salut,

    parcourir une liste de fichiers, et pour chacun regarder le 8ième champ délimité par des ';'.
    Il n'y a qu'une ligne dans tes fichiers ?
    Ou c'est à chaque ligne qu'il faut tester ?

    Sur ce champ-là, je vérifie que c'est une date correcte,
    Qu'appelles-tu une "date correcte" ?

    Merci de poster un exemple concret de ton fichier et de ce que tu veux faire exactement...

    Je ne pense pas que "sed" soit l'outil adéquat ;-\
    0
  2. s€b
     
    Oui désolé je suis tellement dans mon problème que j'ai du mal à être clair...

    Donc mes fichiers ont plusieurs (milliers) de lignes, et sont sous cette forme :
    un;deux;trois;quatre;cinq;six;sept;15/08/2006;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;01/01/2010;neuf;dix

    Donc avec cet exemple, les lignes 2 et 3 sont mal formatées, les lignes 1 et 4 oui. Une date correcte pour moi est (comme l'indique l'expression régulière) : un nombre de 00 à 39, suivi de / suivi d'un nombre de 00 à 19 suivi de / suivi d'un nombre de 0000 à 9999. C'est un peu permissif pour un controle de date, mais ça me suffit.

    Au sujet de sed, je me suis dit que c'était une fonction bien optimisée et donc rapide, et elle me permettrait de réaliser mon traitement plus rapidement qu'avec mon premier script.

    Merci !
    0
  3. bob031 Messages postés 8228 Statut Membre 473
     
    Bonjour,

    qui dit "champ" .... dit awk !

    :-)
    0
  4. s€b
     
    et awk est rapide ? As-tu sous la main un exemple d'utilisation avec parcours de champ ? Juste pour m'aider à démarrer car je ne connais vraiment pas...

    Merci !
    0
  5. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. s€b
     
    Merci, je vais regarder ça, et si j'arrive à quelque chose je poste la solution...
    0
  7. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
     
    Essaye ça :

    [tmpfs]$ cat data
    un;deux;trois;quatre;cinq;six;sept;15/08/2006;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;01/01/2010;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;31/10/2009;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;32/11/2008;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;25/1/2009;neuf;dix
    
    [tmpfs]$ awk -F";" '$8 !~ /([0-2][0-9]|30|31)+\/0[1-9]|1[0-2]\/[0-9]{4}/ { print "Ligne : "NR" pas OK" }' data
    Ligne : 2 pas OK
    Ligne : 3 pas OK
    Ligne : 5 pas OK
    Ligne : 6 pas OK
    Ligne : 7 pas OK
    
    [tmpfs]$

    ;-))
    0
  8. s€b
     
    Merci !
    c'est cela qu'il me fallait effectivement. Par contre lorsque je l'ai imbriqué dans ma boucle for qui parcoure les répertoires pour trouver les fichiers, alors l'expression régulière ne reconnait plus rien, toutes les lignes sont fausses...

    Par contre, je vois un souci aussi dans ton exemple : la ligne 5 devrait être bonne, le 31/10/2009 est une bonne date.
    0
    1. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
       
      Effectivement il y a un problème dans la regex ;-((

      Pas trop le temps là de voir d'où ça vient, désolé ;-(
      0
  9. s€b
     
    Bon j'ai bien avancé, et je vais m'arrêter là pour l'instant, je ne suis pas payé pour passer mes journées là dessus (malheureusement). La commande suivante fonctionne :
    awk -F";" '$8 !~ /([0-3][0-9])\/([0-1][0-9])\/([0-2][0-9][0-9][0-9])/ { print "Fichier "FILENAME" Ligne : "NR" date mauvaise : "$8}' ${fichier} >> ${log}

    En fait, le | n'est pas accepté par awk pour faire un ou, ni les { } pour utiliser le multiplicateur.Est-ce une limitation de gawk ? Je ne sais pas, je laisse le topic ouvert si jipicy ou quelqu'un d'autre veut mettre une solution plsu élégante.

    Et merci à ceux qui m'ont répondu !
    0
  10. lami20j
     
    Salut,

    Je ne sais pas mais c'est peut être le quantificateur {4} qui pose de problèmes

    Voici un exemple sans {4}

    awk -F";" '$8 !~ /([0-2][0-9]|3[01])\/0[1-9]|1[0-2]\/[0-9][0-9][0-9][0-9]/ { print "Ligne : "NR" pas OK " $8}' data


    Ligne : 2 pas OK 5/08/2006
    Ligne : 3 pas OK
    Ligne : 7 pas OK 25/1/2009

    En revanche si un seul champ contient la date, un simple egrep te suffit, pas besoin de awk.
    Le lignes sont affiché avec le numéro devant.

    egrep -vn '([0-2][0-9]|3[01])+\/0[1-9]|1[0-2]\/[0-9]{4}' data
    2:un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    3:un;deux;trois;quatre;cinq;six;sept;;neuf;dix
    7:un;deux;trois;quatre;cinq;six;sept;25/1/2009;neuf;dix
    0
  11. s€b
     
    Non il y a toujours un souci : le 32/11/2009 fonctionne, c'est à dire qu'il n'est pas repéré comme date mauvaise (je ne sais pas pourquoi, à mon avis il s'embrouille au niveau des priorités des opérateurs).

    Par contre bonne idée pour le egrep, mais effectivement j'ai plusieurs dates par lignes.

    Sinon une autre solution, d'un collègue qui "pense autrement" :
    awk 'BEGIN { print "Debut du traitement";
    FS=";"}
    substr($8,1,2)>31 ||substr($8,4,2)>12 ||substr($8,7,4) !~ /([0-9][0-9][0-9][0-9])/ || substr($8,3,1)!= "/" { print "Ligne : "$8" "NR" pas OK " }
    END {print "fin traitement"}
    ' test.txt

    Merci !
    0
  12. lami20j
     
    Salut,

    Pour la regex il fallait mettre le motif pour les dates entre parenthèses. 0[1-9]|1[0-2]

    Pourquoi?
    Sans les parenthèses on lira comme ça

    0[1-9]|1[0-2]
    - cherche un 0 suivi soit d'un chiffre de 1 à 9 soit du chiffre 1 suivi d'un chiffre entre 0 et 2
    Ce n'est pas ça qu'on veut.

    Si on mets le parenthèses (0[1-9]|1[0-2]) alors on lira
    - cherche un 0 suivi d'un chiffre de 1 à 9 ou cherche 1 suivi d'un chiffre de 0 à 2

    Avec awk

    $ cat data
    un;01/1/2010deux;trois;quatre;cinq;six;sept;15/08/2006;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    un;deux;2/5/2009;quatre;cinq;six;sept;;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;01/01/2010;neuf;dix
    un;deux;trois;quatre;7/10/2010;six;sept;31/10/2009;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;32/11/2008;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;25/1/2009;neuf;dix
    $ awk -F";" '$8 !~ /([01][0-9]|2[0-9]|3[01])\/(0[1-9]|1[012])\/[0-9][0-9][0-9][0-9]/ { print "Ligne : "NR" pas OK " $8}' data
    Ligne : 2 pas OK 5/08/2006
    Ligne : 3 pas OK
    Ligne : 6 pas OK 32/11/2008
    Ligne : 7 pas OK 25/1/2009


    Pour grep je saute les 1ers 7 champs et je teste sur le 8ème.
    Avec grep
    $ egrep -vn '^(.*;){7}([01][0-9]|2[0-9]|3[01])\/(0[1-9]|1[012])\/[0-9]{4}' data
    2:un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    3:un;deux;2/5/2009;quatre;cinq;six;sept;;neuf;dix
    6:un;deux;trois;quatre;cinq;six;sept;32/11/2008;neuf;dix
    7:un;deux;trois;quatre;cinq;six;sept;25/1/2009;neuf;dix
    0
  13. lami20j
     
    Re,

    Pour traiter aussi le cas de 00 pour le jour

    $ cat data
    un;01/1/2010deux;trois;quatre;cinq;six;sept;15/08/2006;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    un;deux;2/5/2009;quatre;cinq;six;sept;;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;00/01/2010;neuf;dix
    un;deux;trois;quatre;7/10/2010;six;sept;31/10/2009;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;32/11/2008;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;25/1/2009;neuf;dix
    $ awk -F";" '$8 !~ /(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/[0-9][0-9][0-9][0-9]/ { print "Ligne : "NR" pas OK " $8}' data
    Ligne : 2 pas OK 5/08/2006
    Ligne : 3 pas OK
    Ligne : 4 pas OK 00/01/2010
    Ligne : 6 pas OK 32/11/2008
    Ligne : 7 pas OK 25/1/2009
    $ egrep -vn '^(.*;){7}(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/[0-9]{4}' data
    2:un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    3:un;deux;2/5/2009;quatre;cinq;six;sept;;neuf;dix
    4:un;deux;trois;quatre;cinq;six;sept;00/01/2010;neuf;dix
    6:un;deux;trois;quatre;cinq;six;sept;32/11/2008;neuf;dix
    7:un;deux;trois;quatre;cinq;six;sept;25/1/2009;neuf;dix
    0
  14. lami20j
     
    Re,

    En fait, le | n'est pas accepté par awk pour faire un ou, ni les { } pour utiliser le multiplicateur.Est-ce une limitation de gawk ?

    L'alternative fonctionne bien, mais comme j'ai expliqué dans l'exemple précédent il faut grouper les alternative pour obtenir le résultat voulu. Le moteur des regex n'est pas intelligent, ça seul qualité est la persévérance.

    Pour le quantificateur intervalle {n,m} dans man awk je vois ça

    Interval expressions are only available if either --posix or --re-interval is specified on the command line.

    $ cat data
    un;01/1/2010deux;trois;quatre;cinq;six;sept;15/08/2006;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;5/08/2006;neuf;dix
    un;deux;2/5/2009;quatre;cinq;six;sept;;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;00/01/2010;neuf;dix
    un;deux;trois;quatre;7/10/2010;six;sept;31/10/2009;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;32/11/2008;neuf;dix
    un;deux;trois;quatre;cinq;six;sept;25/1/2009;neuf;dix
    $ awk --posix -F";" '$8 !~ /(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/([0-9]{4})/ { print "Ligne : "NR" pas OK " $8}' data
    Ligne : 2 pas OK 5/08/2006
    Ligne : 3 pas OK
    Ligne : 4 pas OK 00/01/2010
    Ligne : 6 pas OK 32/11/2008
    Ligne : 7 pas OK 25/1/2009
    $ awk --re-interval -F";" '$8 !~ /(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/([0-9]{4})/ { print "Ligne : "NR" pas OK " $8}' data
    Ligne : 2 pas OK 5/08/2006
    Ligne : 3 pas OK
    Ligne : 4 pas OK 00/01/2010
    Ligne : 6 pas OK 32/11/2008
    Ligne : 7 pas OK 25/1/2009
    0
  15. jipicy Messages postés 40842 Date d'inscription   Statut Modérateur Dernière intervention   4 898
     
    Merci l'AMI pour toutes ces belles explications ;-))
    0
  16. bob031 Messages postés 8228 Statut Membre 473
     
    Oh oui ... un grand merci !

    :-)
    0
  17. s€b
     
    Merci beaucoup à tous ! et particulièrement à toi lami20j : je n'aurai jamais trouvé la petite phrase du man donnant ces options !
    0