(sed) extraction d'une partie d'un fichier if
oqp24
Messages postés
10
Statut
Membre
-
oqp24 Messages postés 10 Statut Membre -
oqp24 Messages postés 10 Statut Membre -
Extraction de chaine avec sed
Sur Unix, j’ai un fichier composé de plusieurs paragraphes.
Tous les paragraphes commence par DDD et finissent par FFF.
Certain paragraphes contiennent la chaine XXX.
Exemple
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladd
DDD blablablaqq
blablablasd
FFF blablabla
1 : Comment extraire les paragraphes contenant la chaine XXX.(Parag 2)
2 : Comment extraire les paragraphes ne contenant pas la chaine XXX. (Parag 1 et 3)
sed -en "/^DDD/ , /^FFF/p" -en "/XXX/w resultat" file
extrait tout le fichier moins la ligne contenant XXX
J’ai tourné le sed et le awk dans tous les sens mais je n’avance plus.
Merci
Sur Unix, j’ai un fichier composé de plusieurs paragraphes.
Tous les paragraphes commence par DDD et finissent par FFF.
Certain paragraphes contiennent la chaine XXX.
Exemple
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladd
DDD blablablaqq
blablablasd
FFF blablabla
1 : Comment extraire les paragraphes contenant la chaine XXX.(Parag 2)
2 : Comment extraire les paragraphes ne contenant pas la chaine XXX. (Parag 1 et 3)
sed -en "/^DDD/ , /^FFF/p" -en "/XXX/w resultat" file
extrait tout le fichier moins la ligne contenant XXX
J’ai tourné le sed et le awk dans tous les sens mais je n’avance plus.
Merci
A voir également:
- (sed) extraction d'une partie d'un fichier if
- Fichier bin - Guide
- Comment réduire la taille d'un fichier - Guide
- Comment ouvrir un fichier epub ? - Guide
- Fichier rar - Guide
- Fichier .dat - Guide
9 réponses
Salut,
en fait on n'a même pas besoin d'avoir des paragrphes tant qu'on est sur de ça
commence par DDD et finissent par FFF.
- je récupère le contenu de fichier dans une variable scalaire (mode slurp)
- je fais une recherche progressive de DDD et j'affiche le contenu de la chaine jusqu'à FFF uniquement si quelque part j'ai XXX entre DDD et FFF)
pour ça j'utilise le modificateur g et l'assertion \G
Si je me rappelle bien Dal a des bonnes connaissances en Perl donc il peut corriger mon script. Je suis sous Win maintenant et je ne peux pas tester.
Pour execution
perl /rep/scripts/extrat.pl nom_fichier
lami20j
en fait on n'a même pas besoin d'avoir des paragrphes tant qu'on est sur de ça
commence par DDD et finissent par FFF.
#! /usr/bin/perl
use warnings;use strict;
undef $/;
my $fic = <>;
pos($fic)=0;
while ( $fic =~ /DDD/g ){
if ($fic =~ /\G(.*XXX.*FFF)/){
print $1;
}
}Je ne l'ai pas testé mais l'algo est suivant :
- je récupère le contenu de fichier dans une variable scalaire (mode slurp)
- je fais une recherche progressive de DDD et j'affiche le contenu de la chaine jusqu'à FFF uniquement si quelque part j'ai XXX entre DDD et FFF)
pour ça j'utilise le modificateur g et l'assertion \G
Si je me rappelle bien Dal a des bonnes connaissances en Perl donc il peut corriger mon script. Je suis sous Win maintenant et je ne peux pas tester.
Pour execution
perl /rep/scripts/extrat.pl nom_fichier
lami20j
Bonsoir,
Une astuce pour ne pas trop te pourrir la vie :)
Avec sed seulement, on oublie (travail ligne/ligne, donc pas top)
Avec awk seulement, ça doit être fesable, mais imbittable
Avec les deux ensemble par contre :)
awk travaillant sur des enregistrements et non sur des lignes ( le separateur d'enregistrement est \n par defaut) ça doit être réalisable....
Voyons un peu...
La premiere chose a faire est d'avoir un separateur de champ...
On obtient quelque chose du genre:
IE:
Maintenant, qu'on a ces champ, il suffit de passer ça a awk, en lui demendant d'afficher les enregistrement qui nous sont utiles...
Quelques infos:
Pour ecraser le separateur de champ par defaut, il faut renseigner la variable RS dans un bloque BEGIN tant qu'a faire pour ne le faire qu'une fois au début du traitement
Pour info:
L'expression !/DDD.*XXX.*FFF.*/ ne suffit pas, elle retourne aussi les enregistrement vide (dus aux RECORD_SEP RECORD_SEP généré par sed plus haut)
Donc:
(PS: je sais, c'est optimisable)
Une astuce pour ne pas trop te pourrir la vie :)
Avec sed seulement, on oublie (travail ligne/ligne, donc pas top)
Avec awk seulement, ça doit être fesable, mais imbittable
Avec les deux ensemble par contre :)
awk travaillant sur des enregistrements et non sur des lignes ( le separateur d'enregistrement est \n par defaut) ça doit être réalisable....
Voyons un peu...
La premiere chose a faire est d'avoir un separateur de champ...
sed -e 's/\(^DDD.*\)/RECORD_SEP\1/' -e 's/\(^FFF.*\)/\1RECORD_SEP/' fichierLà on ajoute RECORD_SEP avant les lignes commencant par DDD et aprés les lignes commencant par FFF.
On obtient quelque chose du genre:
RECORD_SEPDDDblablabla blablablalll blablablammmmmmmm FFF blablablassRECORD_SEP RECORD_SEPDDD blablabla blablablaoo lsdqkgsqdXXX blablabla FFF blablabladdRECORD_SEP RECORD_SEPDDD blablablaqq blablablasd FFF blablabla RECORD_SEPSoit 6 enregistrements (C'est la faiblesse), en affinant, on doit pouvoir recuperer 3 enregistrements
IE:
DDDblablabla blablablalll blablablammmmmmmm FFF blablablass RECORD_SEPDDD blablabla blablablaoo lsdqkgsqdXXX blablabla FFF blablabladd RECORD_SEPDDD blablablaqq blablablasd FFF blablablaMais c'est pas grave :)
Maintenant, qu'on a ces champ, il suffit de passer ça a awk, en lui demendant d'afficher les enregistrement qui nous sont utiles...
Quelques infos:
Pour ecraser le separateur de champ par defaut, il faut renseigner la variable RS dans un bloque BEGIN tant qu'a faire pour ne le faire qu'une fois au début du traitement
BEGIN{ RS="RECORD_SEP" }la syntaxe pour afficher un enregistrement s'il correspond a une reg_exp est:/reg_exp/{ print $0} ($0 représente l'enregistrement courant)L'inverse:!/reg_exp/{ print $0}Ce qui nous donne:awk 'BEGIN{ RS="RECORD_SEP" } /DDD.*XXX.*FFF.*/ { print $0 }' pour matcher un paragraphe avec XXX etawk 'BEGIN{ RS="RECORD_SEP" } !/DDD.*XXX.*FFF.*/ && /DDD.*FFF.*/ { print $0 }' pour matcher un paragraphe sans XXX
Pour info:
L'expression !/DDD.*XXX.*FFF.*/ ne suffit pas, elle retourne aussi les enregistrement vide (dus aux RECORD_SEP RECORD_SEP généré par sed plus haut)
Donc:
$ sed -e's/\(^DDD.*\)/RECORD_SEP\1/' -e 's/\(^FFF.*\)/\1RECORD_SEP/' test |awk 'BEGIN{ RS="RECORD_SEP" } /DDD.*XXX.*FFF.*/ { print $0 }'
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladd
$ sed -e's/\(^DDD.*\)/RECORD_SEP\1/' -e 's/\(^FFF.*\)/\1RECORD_SEP/' test |awk 'BEGIN{ RS="RECORD_SEP" } !/DDD.*XXX.*FFF.*/ && /DDD.*FFF.*/ { print $0 }'
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablablaqq
blablablasd
FFF blablablaCa doit repondre à ton attente :)
(PS: je sais, c'est optimisable)
Salut,
Je ne vois pas très bien où tu places tes "paragraphes" 1, 2 et 3 dans ton exemple ci-dessus.
Si ce que tu veux afficher ce sont des lignes (c'est à dire une suite de caractères avec CR au bout), tu peux utiliser grep, et l'option -v ou --invert-match pour les lignes ne contenant pas l'objet de la recherche (du moins dans le GNU grep).
Dal
Je ne vois pas très bien où tu places tes "paragraphes" 1, 2 et 3 dans ton exemple ci-dessus.
Si ce que tu veux afficher ce sont des lignes (c'est à dire une suite de caractères avec CR au bout), tu peux utiliser grep, et l'option -v ou --invert-match pour les lignes ne contenant pas l'objet de la recherche (du moins dans le GNU grep).
Dal
Non ce serai trop simple.
Comment extraire les paragraphes contenant
la chaine XXX .(Parag 2)
on doit obtenir:
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladd
Comment extraire les paragraphes ne contenant pas
la chaine XXX. (Parag 1 et 3)
on doit obtenir:
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablablaqq
blablablasd
FFF blablabla
Comment extraire les paragraphes contenant
la chaine XXX .(Parag 2)
on doit obtenir:
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladd
Comment extraire les paragraphes ne contenant pas
la chaine XXX. (Parag 1 et 3)
on doit obtenir:
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablablaqq
blablablasd
FFF blablabla
Encore une chose si jamais tu essaies le script.
Il faut inverser le sens puisque tu as besoin de Comment extraire les paragraphes ne contenant pas
Donc au lieu de
$fic =~ /\G(.*XXX.*FFF)/
écrit
$fic !~ /\G(.*XXX.*FFF)/
lami20j
Il faut inverser le sens puisque tu as besoin de Comment extraire les paragraphes ne contenant pas
Donc au lieu de
$fic =~ /\G(.*XXX.*FFF)/
écrit
$fic !~ /\G(.*XXX.*FFF)/
lami20j
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
Re,
C'est dommage de ne pas utiliser Perl, car c'est un outil très puissant pour la manipulation de texte.
Je comprend mieux ton concept de "paragraphe".
Je ne connais pas bien awk, mais s'agissant de sed, à ma connaissance celui-ci travaille sur des lignes. Là tes "paragraphes" sont composés d'un nombre indéterminé de lignes avec des délimiteurs situés sur des lignes différentes.
Tu peux cependant faire un script shell qui met dans une variable les "paragraphes" en question en fonction des délimiteurs et qui contrôle pour chaque ligne faisant partie du paragraphe si ton marqueur "XXX" y figure.
Tu fais une boucle (par exemple en bash ou sh) :
while read ligne
do
(...)
done <les_datas.txt
et dedans tu mets tes tests sed sur chaque $ligne, la variable de stockage des paragraphes (initialisée si c'est un marqueur de début, mise à jour sinon), un drapeau si le marqueur "XXX" est présent, un test d'affichage du contenu de la variable de stockage du paragraphe courant en fonction du drapeau (si le marqueur de fin de paragraphe est atteint), etc.
Je n'ai pas le temps de te faire le script tout cuit, mais c'est comme çà que je ferai.
Dal
C'est dommage de ne pas utiliser Perl, car c'est un outil très puissant pour la manipulation de texte.
Je comprend mieux ton concept de "paragraphe".
Je ne connais pas bien awk, mais s'agissant de sed, à ma connaissance celui-ci travaille sur des lignes. Là tes "paragraphes" sont composés d'un nombre indéterminé de lignes avec des délimiteurs situés sur des lignes différentes.
Tu peux cependant faire un script shell qui met dans une variable les "paragraphes" en question en fonction des délimiteurs et qui contrôle pour chaque ligne faisant partie du paragraphe si ton marqueur "XXX" y figure.
Tu fais une boucle (par exemple en bash ou sh) :
while read ligne
do
(...)
done <les_datas.txt
et dedans tu mets tes tests sed sur chaque $ligne, la variable de stockage des paragraphes (initialisée si c'est un marqueur de début, mise à jour sinon), un drapeau si le marqueur "XXX" est présent, un test d'affichage du contenu de la variable de stockage du paragraphe courant en fonction du drapeau (si le marqueur de fin de paragraphe est atteint), etc.
Je n'ai pas le temps de te faire le script tout cuit, mais c'est comme çà que je ferai.
Dal
N'ayant pas le choix, j'ai deja commencé un truc dans ce genre.
Dommage de ne pas avoir la commande toute simple qui tue, car les fichiers à traiter sont énormes.
Merci tout de même.. En esperant qu'un petit genie...
Dommage de ne pas avoir la commande toute simple qui tue, car les fichiers à traiter sont énormes.
Merci tout de même.. En esperant qu'un petit genie...
Salut Adrien,
Chapeau bas Mister Severe ;-))
Effectivement comme l'a dit lami20j, tu m'ôtes des maux de la boîte cranienne ;-))
Bon en attendant en essayant d'optimiser (un tant soit peu) je suis arrivé à ça :
Chapeau bas Mister Severe ;-))
Effectivement comme l'a dit lami20j, tu m'ôtes des maux de la boîte cranienne ;-))
Bon en attendant en essayant d'optimiser (un tant soit peu) je suis arrivé à ça :
sed 's/\(DDD\)/==\1/' fich.txt | awk 'BEGIN { RS="==" } !/XXX/ { print $0 }'
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablablaqq
blablablasd
FFF blablabla
$ sed 's/\(DDD\)/==\1/' fich.txt | awk 'BEGIN { RS="==" } /XXX/ { print $0 }'
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladdMerci Adrien.
Bravo !
Content de te revoir sur le forum :)
En fait, on peut même se passer de sed, puisque "DDD" est déjà un délimiteur :) .. on pourrai simplement le remettre dans l'instruction print.
Enfin, pour que celà marche (cette syntaxe, comme celle de jipicy), il faut que les délimiteurs DDD et FFF ne risquent pas de se retrouver ailleurs dans le corps du paragraphe et que l'on n'ait donc pas à vérifier qu'ils soient en début de ligne.
Dal
Content de te revoir sur le forum :)
En fait, on peut même se passer de sed, puisque "DDD" est déjà un délimiteur :) .. on pourrai simplement le remettre dans l'instruction print.
$ awk 'BEGIN { RS="DDD" } /XXX/ { print "DDD"$0 }' < fich.txt
DDD blablabla
blablablaoo
lsdqkgsqdXXX blablabla
FFF blablabladd
$ awk 'BEGIN { RS="DDD" } \!/XXX/ && /.*FFF.*/ { print "DDD"$0 }' < testsed.txt
DDDblablabla
blablablalll
blablablammmmmmmm
FFF blablablass
DDD blablablaqq
blablablasd
FFF blablabla
Enfin, pour que celà marche (cette syntaxe, comme celle de jipicy), il faut que les délimiteurs DDD et FFF ne risquent pas de se retrouver ailleurs dans le corps du paragraphe et que l'on n'ait donc pas à vérifier qu'ils soient en début de ligne.
Dal
Salut,
on peut même se passer de sed, puisque "DDD" est déjà un délimiteur
C'est exactement à ça que j'ai pensé, mais t'es plus rapide que moi.
Awk je ne connais pas du tout, donc hier soir après la solution d'asevere j'ai commencé à lire un peu awk et ce matin en train d'aller au boulot j'ai pense "mais pourquoi on crée un delimiteur s'il existe déjà". Mais comme tu le dit, ça ne marche que si les délimiteurs DDD et FFF
De toute façon parser du texte ça veut dire parser de texte. Maintenant il faut voir les capacités des outils qu'on utilise.
Et Perl n'est qu'un fils de sed et awk, qui c'est vrai a bien agrandi depuis....
lami20j
on peut même se passer de sed, puisque "DDD" est déjà un délimiteur
C'est exactement à ça que j'ai pensé, mais t'es plus rapide que moi.
Awk je ne connais pas du tout, donc hier soir après la solution d'asevere j'ai commencé à lire un peu awk et ce matin en train d'aller au boulot j'ai pense "mais pourquoi on crée un delimiteur s'il existe déjà". Mais comme tu le dit, ça ne marche que si les délimiteurs DDD et FFF
De toute façon parser du texte ça veut dire parser de texte. Maintenant il faut voir les capacités des outils qu'on utilise.
Et Perl n'est qu'un fils de sed et awk, qui c'est vrai a bien agrandi depuis....
lami20j
Je suis sur une tres grosse prod et il y a donc bcp de restriction.
Dommage
d'accord mais Perl est integré dans les système Unix donc normalement tu ne dois pas avoir des problèmes.
En fait les seul problèmes que tu peux avoir c'est la version de Perl, puisque les regex de Perl on bien evoulées.
lami20j