Parcours de fichiers avec sed

Fermé
s€b - 9 déc. 2009 à 16:00
 s€b - 11 déc. 2009 à 09:29
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 !
A voir également:

17 réponses

jipicy Messages postés 40842 Date d'inscription jeudi 28 août 2003 Statut Modérateur Dernière intervention 10 août 2020 4 897
9 déc. 2009 à 16:16
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
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
bob031 Messages postés 8158 Date d'inscription samedi 7 août 2004 Statut Membre Dernière intervention 1 septembre 2014 472
9 déc. 2009 à 16:35
Bonjour,

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

:-)
0
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

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
bob031 Messages postés 8158 Date d'inscription samedi 7 août 2004 Statut Membre Dernière intervention 1 septembre 2014 472
9 déc. 2009 à 16:45
pas mes cours awk sous le coude pour l'instant !

regarderai ce soir !

en attendant si cela peut t'aider (il y a des exemples) :

https://www.funix.org/fr/unix/awk.htm

http://www.shellunix.com/awk.html
0
Merci, je vais regarder ça, et si j'arrive à quelque chose je poste la solution...
0
jipicy Messages postés 40842 Date d'inscription jeudi 28 août 2003 Statut Modérateur Dernière intervention 10 août 2020 4 897
9 déc. 2009 à 17:35
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
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
jipicy Messages postés 40842 Date d'inscription jeudi 28 août 2003 Statut Modérateur Dernière intervention 10 août 2020 4 897
10 déc. 2009 à 11:43
Effectivement il y a un problème dans la regex ;-((

Pas trop le temps là de voir d'où ça vient, désolé ;-(
0
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
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
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
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
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
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
jipicy Messages postés 40842 Date d'inscription jeudi 28 août 2003 Statut Modérateur Dernière intervention 10 août 2020 4 897
10 déc. 2009 à 20:31
Merci l'AMI pour toutes ces belles explications ;-))
0
bob031 Messages postés 8158 Date d'inscription samedi 7 août 2004 Statut Membre Dernière intervention 1 septembre 2014 472
10 déc. 2009 à 20:42
Oh oui ... un grand merci !

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