Parcours de fichiers avec sed
s€b
-
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 !
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:
- Parcours de fichiers avec sed
- Explorateur de fichiers - Guide
- Renommer des fichiers en masse - Guide
- Fichiers epub - Guide
- Gestionnaire de fichiers - Télécharger - Gestion de fichiers
- Wetransfer gratuit fichiers lourd - Guide
17 réponses
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 ;-\
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 ;-\
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 !
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 !
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 !
Merci !
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
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
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
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]$
;-))
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.
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.
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 !
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 !
Salut,
Je ne sais pas mais c'est peut être le quantificateur {4} qui pose de problèmes
Voici un exemple sans {4}
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.
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
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 !
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 !
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
Pour grep je saute les 1ers 7 champs et je teste sur le 8ème.
Avec grep
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
Re,
Pour traiter aussi le cas de 00 pour le jour
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
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.
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