(sed) extraction d'une partie d'un fichier if

Fermé
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006 - 1 juin 2006 à 14:07
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006 - 6 juin 2006 à 10:42
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
A voir également:

9 réponses

lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
1 juin 2006 à 14:53
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.

#! /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
1
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006
1 juin 2006 à 15:05
C'est sympa mais je ne dois utiliser que du shell unix, pas de Perl.
Je suis sur une tres grosse prod et il y a donc bcp de restriction.

Dommage
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567 > oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006
1 juin 2006 à 15:07
Re,

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
0
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006 > oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006
1 juin 2006 à 15:13
En fait cela fait partie d'un gros projet et donc le shell unix est imposé...et en + je ne connais pas Perl...
0
asevere Messages postés 13084 Date d'inscription lundi 28 janvier 2002 Statut Webmaster Dernière intervention 3 février 2022 426
1 juin 2006 à 20:14
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...
sed -e 's/\(^DDD.*\)/RECORD_SEP\1/' -e 's/\(^FFF.*\)/\1RECORD_SEP/' fichier
Là 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_SEP
Soit 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 blablabla
Mais 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 et
awk '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 blablabla
Ca doit repondre à ton attente :)

(PS: je sais, c'est optimisable)
1
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
1 juin 2006 à 20:19
Salut,

ça fait un bail. Commen tu vas?

Je crois que jipicy n'aura plus des maux de têtes.

Je suis trop paresseux pour faire un truc pareil. Félicitations!

lami20j
0
asevere Messages postés 13084 Date d'inscription lundi 28 janvier 2002 Statut Webmaster Dernière intervention 3 février 2022 426 > lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019
1 juin 2006 à 21:27
Ava bien,

Beaucoup de boulot, alors je post moins :)
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
1 juin 2006 à 14:19
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
0
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006
1 juin 2006 à 14:45
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
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
1 juin 2006 à 15:09
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
0

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

Posez votre question
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006
1 juin 2006 à 15:16
Argh stop pitié pas de PERL
Vos solution sont surement bonne mais hélas ne me servent pas.
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
1 juin 2006 à 15:36
stop pitié pas de PERL ok, je ne dit plus rien.

lami20j
0
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006 > lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019
1 juin 2006 à 15:45
Merci quand meme
Have a good time
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
1 juin 2006 à 17:44
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
0
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006
1 juin 2006 à 17:56
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...
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 894
1 juin 2006 à 21:29
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 :
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 blablabladd
Merci Adrien.
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
2 juin 2006 à 10:19
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.
$ 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
0
lami20j Messages postés 21331 Date d'inscription jeudi 4 novembre 2004 Statut Modérateur, Contributeur sécurité Dernière intervention 30 octobre 2019 3 567
2 juin 2006 à 11:18
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
0
oqp24 Messages postés 10 Date d'inscription mercredi 31 mai 2006 Statut Membre Dernière intervention 6 juin 2006
6 juin 2006 à 10:42
Bravo Severe et merci pour ton aide ca marche nickel.

Merci aussi aux autres mais helas comme l'a si bien dit DAL, les delimitteurs, pour mon cas peuvent se retrouver n'importe ou dans le fichier.

A+
0