Contenu | Rechercher | Menus

Annonce

L'équipe des administrateurs et modérateurs du forum vous invite à prendre connaissance des nouvelles règles.
En cas de besoin, vous pouvez intervenir dans cette discussion.

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 08/01/2018, à 18:33

slash-z

awk plus efficace ? <résolu>

Salut,

Mon souci est le suivant:
Je cherche à extraire certains contenus (quand ils existent) d'un grand nombre de fichiers sources (+ de 5000) en ligne de commande.

Voilà les données précises du problème:
1/ Certains fichiers n'ont rien à extraire.
2/ Le contenu à extraire est sur une ou plusieurs lignes quand il existe.
3/ Il est délimité par une borne de début (qui est unique dans le fichier mais n'est pas toujours présente) et une borne de fin (qui est toujours unique et toujours présente).
4/ quand les 2 bornes existent, j'extrais la ou les lignes entre ces deux bornes.
5/ je souhaite que chaque ligne extraite soit précédée du nom du fichier, suivi par ":" et d'un numéro de ligne (idéalement le n° de ligne dans le fichier source mais ça peut être un simple compteur incrémenté à la limite) puis de nouveau ":". Les ":" me servent de délimiteurs mais ça peut être n'importe quoi d'autre, je m'en fous.

Je me suis bidouillé un script à coup de grep et de sed qui fait à peu près le job (désolé c'est sûr une seule ligne de commande pour faciliter mes tests):

ls REPERTOIRE/* | while read ligne; do N1=$(grep -n 'DEBUT>' $ligne | sed 's/:.*$//'); if [ "$N1" != "" ]; then N2=$(grep -n 'FIN' $ligne | sed 's/:.*$//'); if [ "$N2" != "" ]; then N1=$(expr $N1 + 1); N2=$(expr $N2 - 1); if [ "$N1" -le "$N2" ]; then if [ "$N1" -eq "$N2" ]; then echo "$ligne:" | tr -d '\n'; sed -n "${N1}, ${N2}p" < $ligne; else echo "$ligne:" | tr -d '\n'; sed -n "${N1}, ${N2}p" < $ligne | tr '\n' ' ' | sed 's/$/\n/'; echo "${ligne}: sur plusieurs lignes $N1 $N2" >> /dev/stderr; fi; else echo "${ligne}: ${N1} doit être inférieur ou égal à $N2" >> /dev/stderr; fi; else echo "${ligne}: pas de fin trouvée" >> /dev/stderr; fi; else echo "${ligne}: pas de début trouvé" >> /dev/stderr; fi; done

MAIS: actuellement, je concatène toutes les lignes à extraire dans une seule ligne et je n'ai pas de compteur.
De plus, ma bidouille est super lente à exécuter (sans doute + d'une heure à vue de nez pour analyser les 5000 fichiers sources)

Je pense que awk
1/ est peut-être plus efficace et rapide.
2/ serait capable de faire exactement le taf selon mes exigences exposées plus haut (notamment le point 5/, ce qui n'est pas le cas actuellement.)

Problème: je n'ai même pas les connaissances de base pour utiliser awk !!! oups.
Ce serait l'occasion de me plonger enfin dans la doc de awk mais j'ai peur d'aboutir à un truc qui ne soit pas suffisamment fiable à l'arrivée pour avoir confiance.

Si quelqu'un veut bien m'aider ?

Dernière modification par slash-z (Le 14/01/2018, à 01:08)

Hors ligne

#2 Le 08/01/2018, à 19:38

pingouinux

Re : awk plus efficace ? <résolu>

Bonsoir,
Je pense que python est bien adapté à ce que tu veux faire.

#!/usr/bin/python3
import re, sys

fic=sys.argv[1]
deb=re.compile('DEBUT')
fin=re.compile('FIN')

with open(fic,'r') as f:
   lig_deb=lig_fin=False; num=0; s=''
   for lig in f:
      num+=1
      if deb.search(lig): lig_deb=True; continue
      if fin.search(lig): lig_fin=True; break 
      if lig_deb: s+="%s:%d:%s"%(fic,num,lig)

if lig_deb and lig_fin: sys.stdout.write(s)

À appeler ainsi

for fichier in REPERTOIRE/*
do
   ./le_script.py "$fichier"
done

Peut-être faudra-t-il l'adapter un peu pour obtenir exactement ce que tu veux. Je n'ai pas testé les performances.

Édité : Correction

Dernière modification par pingouinux (Le 08/01/2018, à 19:45)

Hors ligne

#3 Le 08/01/2018, à 19:58

slash-z

Re : awk plus efficace ? <résolu>

Super merci à toi pingouinux !
Mais j'ai oublié de préciser dans mes préconisations qu'il est obligatoire que ce soit un script shell. C'est une contrainte forcée.

Par ailleurs dans l'idée du compteur exposée au point 5/, idéalement aussi: il faudrait que le compteur soit implémenté sur 5 chiffres quel que soit le n° de ligne. Exemple de lignes en résultat formatées comme je le souhaiterais:

./REPERTOIRE/FILE.html:00472:        <div><h2>Truc bidule chose</h2>
./REPERTOIRE/FILE.html:00473:                <br>truc machin
./REPERTOIRE/FILE.html:00474:                <br>machin bidule 

Sur 5 chiffres avec un remplissage de zéros devant, pour faciliter un "sort" dans d'autres scripts plus tard.

Dernière modification par slash-z (Le 08/01/2018, à 20:02)

Hors ligne

#4 Le 08/01/2018, à 20:15

Watael

Re : awk plus efficace ? <résolu>

il est obligatoire que ce soit un script shell

awk est un langage à part entière, comme python.


eval, c'est mal.

Hors ligne

#5 Le 08/01/2018, à 20:58

pingouinux

Re : awk plus efficace ? <résolu>

slash-z #3 a écrit :

il faudrait que le compteur soit implémenté sur 5 chiffres

Il suffit de remplacer

      if lig_deb: s+="%s:%d:%s"%(fic,num,lig)

par

      if lig_deb: s+="%s:%05d:%s"%(fic,num,lig)

Hors ligne

#6 Le 08/01/2018, à 21:17

slash-z

Re : awk plus efficace ? <résolu>

Ok, bon il faut que j'explique le souci avec python.
Je travaille actuellement sur une machine qui n'est pas la mienne sous windows.
Et j'utilise "Git" pour continuer mes petits travaux perso en shell. Mais je ne crois pas que python fasse partie du kit (?). Et puis comme ce n'est pas ma machine, je ne veux pas trop faire n'importe quoi avec. Et puis je n'ai pas trop de temps à investir sur ce genre de problèmes pour le moment.
D'où: je me limite au shell bash scripto sensu,

Dernière modification par slash-z (Le 08/01/2018, à 21:18)

Hors ligne

#7 Le 09/01/2018, à 20:17

slash-z

Re : awk plus efficace ? <résolu>

Pas de spécialiste de awk qui passerait par là ?

Hors ligne

#8 Le 09/01/2018, à 22:38

Watael

Re : awk plus efficace ? <résolu>

je n'ai pas trop de temps à investir sur ce genre de problèmes pour le moment.


eval, c'est mal.

Hors ligne

#9 Le 09/01/2018, à 22:52

slash-z

Re : awk plus efficace ? <résolu>

T'inquiète pas Watael, c'est pas grave.

Hors ligne

#10 Le 10/01/2018, à 16:18

kholo

Re : awk plus efficace ? <résolu>

salut,
je te met ma version éclatée :

motif1='DEBUT>'
motif2='FIN'

for fichier in REPERTOIRE/*;
do
	echo "===============================================";
	echo "lecture fichier ${fichier}";
	numL=0;
	N1=0;
	N2=0;
	while read ligne; 
	do 
		((numL++));

		if [ "$(grep -n "${motif1}" <<< "${ligne}" | sed 's/:.*$//')" != "" ]; 
		then 
			N1=$numL;
		# else 
			# echo "$ {ligne}: pas de ${motif1} trouvé" >> /dev/stderr; 
		fi ; 

		if [ "$(grep -n "${motif2}" <<< "${ligne}" | sed 's/:.*$//')" != "" ]; 
		then 
			# echo "${motif2} trouvé ligne $numL";
			N2=$numL;
			if [ $N1 -le $N2 ]; 
			then 
				if [ $N1 -eq $N2 ]; 
				then 
					echo "trouvé sur la même ligne $N1" >> /dev/stderr; 
					sed -n "s/.*${motif1}\(.*\)${motif2}.*/\1/p" <<< "${ligne}"
				else 
					echo "sur plusieurs lignes $N1 $N2" >> /dev/stderr; 
					reponse="$(sed "$N1,$N2!d" "${fichier}")" # récupère de la ligne N1 à N2
					reponse="${reponse#*${motif1}}" # tout ce qu'il y a après motif1
					reponse="${reponse%%${motif2}*}" # tout ce qu'il y a avant motif2
					echo "$reponse"
				fi; 
			# else 
				# echo "${ligne}: ${N1} doit être inférieur ou égal à $N2" >> /dev/stderr; 
			fi; 
		# else 
			# echo "${ligne}: pas de fin trouvée" >> /dev/stderr; 
		fi; 
	done < "${fichier}"
done

Hors ligne

#11 Le 10/01/2018, à 17:28

slash-z

Re : awk plus efficace ? <résolu>

Super, merci àtoi kholo,

Je vais tester ça ce soir. Je suis pas sûr à vue de nez que ton code fasse le préfixage de chaque ligne par le nom du fichier  comme expliqué au point 5/ et puis j'aime pas trop mettre des variables dans les sed mais je vais voir tout ça.

De toute façon ma commande de départ a été écrite comme un cochon et donc très certainement optimisable même sans awk, j'ai deux trois idées en tête.

Merci beaucoup en tout, c'est gentil d'y avoir passé un peu de temps.

En fait je voulais simplement savoir de gens qui maîtrisent awk (ce qui n'est pas mon cas) s'il y existait une manière plus simple et rapide de faire le job demandé. Et auquel cas me donner quelques indications.

Dans 2 jours, je clos le sujet de toute façon.

@Watael, je comprends pas pourquoi tu t'es excité comme ça après moi. Mais il faut te calmer, l'ami !
Tu sais, il existe une vie enrichissante et passionnante en dehors de l'informatique aussi, si si ! je te jure, essaye tu verras. Et puis ça te rendra plus sociable, c'est pas mal non plus.
Ceci dit, c'est sans rancune.

Hors ligne

#12 Le 10/01/2018, à 18:10

kholo

Re : awk plus efficace ? <résolu>

oui, j'ai un peu lu en travers
mais tu auras déjà quelques idées... cool
le plus chient était de trouver une routine qui récupère entre deux balises...
ça m' a permis de bosser un peu ça
mais j'ai rien trouvé de simple du point de vue de ta demande...
en même temps, je suis nul avec sed et awk...
on va pouvoir évoluer la dessus ensemble...

Hors ligne

#13 Le 10/01/2018, à 18:24

Watael

Re : awk plus efficace ? <résolu>

où ça excité ?

msg1: je t'explique que faire ce que tu veux en awk, ce ne sera pas plus du shell qu'en python.
msg2: je reprends tes propos pour expliquer pourquoi je ne m'impliquerai pas davantage.

touuuut ça très posément.

quand je suis énervé, JE SUIS ÉNERVÉ !!!


eval, c'est mal.

Hors ligne

#14 Le 10/01/2018, à 22:13

slash-z

Re : awk plus efficace ? <résolu>

Watael a écrit :

où ça excité ?

msg1: je t'explique que faire ce que tu veux en awk, ce ne sera pas plus du shell qu'en python.
msg2: je reprends tes propos pour expliquer pourquoi je ne m'impliquerai pas davantage.

touuuut ça très posément.

quand je suis énervé, JE SUIS ÉNERVÉ !!!

OK watael, je t'ai peut-être surestimé en me disant que comme beaucoup d'informaticiens t'étais sûrement un peu monomaniaque et un peu trop stressé. En réalité t'es peut-être juste... comment le dire gentiment ? Chais pas et à vrai dire c'est pas mon problème smile
Basta

Hors ligne

#15 Le 13/01/2018, à 11:54

LeoMajor

Re : awk plus efficace ? <résolu>

salut,
exemple;

cd /usr/share/doc/nvidia-384/html/
for fich in ./*.html; do awk '/<head/ {deb=1}; {if(deb==1)res[NR]=$0}; /[nN]vidia|NVIDIA/ {if(deb==1){for (r in res)printf("%s:%05d:%s\n",FILENAME,r,res[r]); exit } }' "$fich"; done

./acknowledgements.html:00003:<head>
./acknowledgements.html:00004:<meta name="generator" content=
./acknowledgements.html:00005:"HTML Tidy for Linux/x86 (vers 1 September 2005), see www.w3.org">
./acknowledgements.html:00006:<meta http-equiv="Content-Type" content=
./acknowledgements.html:00007:"text/html; charset=us-ascii">
./acknowledgements.html:00008:<title>Chapter&nbsp;36.&nbsp;Acknowledgements</title>
./acknowledgements.html:00009:<meta name="generator" content="DocBook XSL Stylesheets V1.68.1">
./acknowledgements.html:00010:<link rel="start" href="index.html" title=
./acknowledgements.html:00011:"NVIDIA Accelerated Linux Graphics Driver README and Installation Guide">
./addressingcapabilities.html:00003:<head>
./addressingcapabilities.html:00004:<meta name="generator" content=
./addressingcapabilities.html:00005:"HTML Tidy for Linux/x86 (vers 1 September 2005), see www.w3.org">
./addressingcapabilities.html:00006:<meta http-equiv="Content-Type" content=
./addressingcapabilities.html:00007:"text/html; charset=us-ascii">
./addressingcapabilities.html:00008:<title>Chapter&nbsp;34.&nbsp;Addressing Capabilities</title>
./addressingcapabilities.html:00009:<meta name="generator" content="DocBook XSL Stylesheets V1.68.1">
./addressingcapabilities.html:00010:<link rel="start" href="index.html" title=
./addressingcapabilities.html:00011:"NVIDIA Accelerated Linux Graphics Driver README and Installation Guide">
./addtlresources.html:00003:<head>
./addtlresources.html:00004:<meta name="generator" content=
./addtlresources.html:00005:"HTML Tidy for Linux/x86 (vers 1 September 2005), see www.w3.org">
./addtlresources.html:00006:<meta http-equiv="Content-Type" content=
./addtlresources.html:00007:"text/html; charset=us-ascii">
./addtlresources.html:00008:<title>Chapter&nbsp;35.&nbsp;NVIDIA Contact Info and Additional
...
..

Hors ligne

#16 Le 13/01/2018, à 12:04

Watael

Re : awk plus efficace ? <résolu>

pourquoi est-ce si compliqué ?

gawk '/debut/,/fin/printf("%s:%05d:%s\n",FILENAME,FNR,$0)' /chemin/*.ext

eval, c'est mal.

Hors ligne

#17 Le 14/01/2018, à 01:07

slash-z

Re : awk plus efficace ? <résolu>

Merci LeoMajor, je viens de tester c'est parfait et super rapide.

Merci à toi aussi Watael, mais il y a un problème de syntaxe ?

$ gawk '/Connexion/,/Connexion/printf("%s:%05d:%s\n",FILENAME,FNR,$0)' ./*.html
gawk: cmd. line:1: /Connexion/,/Connexion/printf("%s:%05d:%s\n",FILENAME,FNR,$0)
gawk: cmd. line:1:                        ^ syntax error
gawk: cmd. line:2: /Connexion/,/Connexion/printf("%s:%05d:%s\n",FILENAME,FNR,$0)
gawk: cmd. line:2:                                                              ^ unexpected newline or end of string

Hors ligne

#18 Le 14/01/2018, à 01:49

Watael

Re : awk plus efficace ? <résolu>

oops, « j'ai omis les œufs de caille !? »

gawk '/debut/,/fin/{ printf("%s:%05d:%s\n",FILENAME,FNR,$0) }' /chemin/*.ext

Dernière modification par Watael (Le 14/01/2018, à 01:52)


eval, c'est mal.

Hors ligne

#19 Le 14/01/2018, à 16:52

slash-z

Re : awk plus efficace ? <résolu>

Bilan, j'ai de gros soucis de localisation avec gawk que je n'arrive pas à résoudre. (Environnement: "git" sous windows et j'y ai déjà passé plusieurs heures)
Dommage parce que c'est encore (beaucoup) plus rapide.
Mais je pense que le gain en vitesse d'exécution s'explique en grande partie par l'économie de la boucle for.
Gain qui sera annulé de toute façon dans la version définitive parce qu'il s'agit en fait d'une arborescence à explorer. Donc la commande définitive sera de la forme find ./ -type f -exec awk...

Merci beaucoup en tout cas de votre aide à tous.
J'en reste là et ça me convient parfaitement.

Hors ligne

#20 Le 14/01/2018, à 17:16

Watael

Re : awk plus efficace ? <résolu>

Mais je pense que le gain en vitesse d'exécution s'explique en grande partie par l'économie de la boucle for.

pas seulement, le shell est lent.

Gain qui sera annulé de toute façon dans la version définitive parce qu'il s'agit en fait d'une arborescence à explorer. Donc la commande définitive sera de la forme find ./ -type f -exec awk...

tu devrais pouvoir traiter tous les fichiers trouvés "d'un seul coup" en utilisant +, à la place de \;, qui, lui, ne traite q'un fichier à la fois.


eval, c'est mal.

Hors ligne

#21 Le 14/01/2018, à 18:21

slash-z

Re : awk plus efficace ? <résolu>

Je connaissais pas cette syntaxe avec "+".

J'ai donc executé ça:

$ find ./REPERTOIRE -type f -exec awk '/DEBUT/ {deb=1}; {if(deb==1)res[NR]=$0}; /FIN/ {if(deb==1){for (r in res)printf("%s:%05d:%s\n",FILENAME,r,res[r]); exit } }' {} \; >./TEST1.txt

Puis j'ai testé ça:

$ find ./REPERTOIRE -type f -exec awk '/DEBUT/ {deb=1}; {if(deb==1)res[NR]=$0}; /FIN/ {if(deb==1){for (r in res)printf("%s:%05d:%s\n",FILENAME,r,res[r]); exit } }' {} + >./TEST2.txt

Sachant que ./REPERTOIRE en fait d'arborescence est composé de 4507 sous-répertoires qui contiennent chacun entre 2 et 4 fichiers à traiter et c'est tout. Le nom des sous-répertoires est en fait un identifant numérique de 1 à 5974 avec quelques "trous".

Je ne comprends pas ce que j'obtiens dans la version avec le +. Le "exec" semble n'avoir traité que 9 répertoires. smile

Je suis en train de lire cette doc https://linuxaria.com/howto/linux-shell … h-examples pour essayer de comprendre comment ça fonctionne.

Bon ^^

Dernière modification par slash-z (Le 14/01/2018, à 18:22)

Hors ligne