Se connecter avec
S'enregistrer | Connectez-vous
Résolu

Extraction de lignes particulières d'un fichier

Dernière réponse : dans Le monde de Linux
Partagez

Bonjour,

j'ai un fichier (.log) dans lequel en gros j'ai des patterns de données par exemple de 4 lignes :
1.blablabla texte
2.données 1
3.données 2
4.ligne vide
5.blablabla texte
6.données 1
7.données 2
8.ligne vide
9.etc...


bref j'ai des tas de ligne et ce que je voudrais c'est extraire par exemple une ligne sur 4 à partir de la 2e ligne pour placer toutes les données 1 (ou données2, et même le texte) du pattern dans un fichier txt par exemple.
(En fait même l'idéal, plutôt que de faire 3 scripts pour récupérer les 3 lignes à chaque fois dans 3 fichiers différents puis de les concaténer avec paste ensuite, ce serait de faire ça en une fois dans mon script)

Et là je bloque, j'ai regardé sur le net sur d'autres forums pour tenter de m'inspirer d'utilisation de fonctions genre awk ou grep mais je n'arrive pas à les utiliser de façon à faire ce que je souhaite faire.
Voici un extrait de mon script (mais qui en plus n'est pas reconnu, quand je le lance ça me fait une erreur "command not found") :

testscript
  1. #! /bin/bash
  2.  
  3. for i in 'seq 1 194' ;
  4. x=4*i - 1
  5.  
  6. do
  7. awk 'NR==x' fichier.log > données1.txt
  8. done


ou aussi j'ai tenté une une version alternative mais qui ne marche pas non plus :

  1. awk ' BEGIN
  2. {
  3. for (i=1;i<=194;i++)
  4. {
  5. x=i*4 - 1
  6. print $x
  7. }
  8. }
  9. END ' /home/chemin/fichier.log > données1.txt


je précise que quand je le lance je suis dans le répertoire où il est, que je l'ai rendu exécutable en faisant "chmod a+x testscript".
Le 194 c'est parce que y'a 194 patterns de 4 lignes, donc j'ai voulu boucler ça mais je suis pas sûr de m'y être bien pris.

Mon fichier log si ça peut vous aider ressemble à ça :

  1. Apr 15 11:49 [nomdufichiertraité1.fits[*,1]]:
  2. 1974.419 1.117 -1.26 1.126 -1.62 73.02 0.
  3. ( 764.87) ( ) (9.4) ( 8.427) (2.7) ( 395.4) ( 0.)
  4.  
  5. Apr 15 11:49 [nomdufichiertraité2.fits[*,1]]:
  6. 1984.702 1.265 -8.55 0.676 -9.72 8.268 0.
  7. ( 1.6385) ( ) (3.2) ( 0.2504) (3.6) ( 4.219) ( 0.)
  8.  
  9. etc...


Ce que je voudrais faire pour résumer donc, c'est créer un fichier de 3 colonnes et 194 lignes contenant :
- en 1ere colonne le nom du fichier traité (donc là je suppose que je dois boucler un cut -c en précisant d'où à où je coupe de sorte à ne pas garder les "[" ainsi que la date et le reste des caractères non désirés, ou alors un grep?)
- en 2e colonne une donnée particulière de la 2e ligne (par exemple la 3e donnée, là aussi je vais de voir utiliser un cut je pense, ou un awk même puisqu'elles sont déjà en colonnes)
- en 3e colonne idem une donnée particulière entre parenthèses de la 3e ligne, par exemple la 3e donnée aussi si je décide de sélectionner la 3e sur la ligne du dessus



Voilà en gros mon problème, dans les scripts que j'ai commencé à faire je me contentais d'abord d'essayer d'extraire une ligne particulière, alors déjà que je n'y suis pas arrivé je n'ai pas encore essayé de faire le reste, mais si vous avez des pistes cela m'aiderait beaucoup !

merci d'avance de votre aide et n'hésitez pas à me redemander des trucs si je n'ai pas été assez clair ou pas assez complet ;) 

Julien

Sans rentrer dans le détail de ton script pour l'instant, concernant son éxécution tu utilises bien la commande suivante :

./testscript

Etudie ce petit tout petit script awk :

  1. BEGIN {I=1}
  2. {print > "fichier_n"I".txt" ; I++ }


A toi maintenant de faire des opérations pour faire évoluer I en fonction de tes besoins.
Propose-nous tes réflexions, on en discutera.
Contenus similaires
Posez votre question

merci pour vos réponses !

sinon oui là je me sens bien stupide : l'habitude Matlab d'exécuter les scripts juste en tapant le nom, j'avais bel et bien oublié de taper le ./ avant le nom...
pour les balises "code" j'avais pas vu désolé, je vais y faire attention maintenant !

je vais voir tout ça d'ici demain car là j'ai pas trop le temps et je vous tiens au courant assez rapidement alors ;) 



@ Zeb : alors j'ai essayé ton petit script awk et là en fait ça me créé juste des fichiers qui sont nommés en fonction de I qui croit jusqu'à ce que j'arrête de faire tourner le script.

J'ai donc remarqué que dans mes scripts il fallait des instructions dans le BEGIN et END, ce que j'avais oublié de faire.

J'ai tenté ça :
  1. awk ' BEGIN {I=1}
  2. {
  3. x=(I*4)-1
  4. { print $x : I++ }
  5. }
  6. END{I<=194} ' /home/chemin/fichier.log > fichier.txt



mais ça me retourne une erreur de syntaxe, je vois pas trop comment arriver à extraire ces lignes, comment gérer la sélection à partir de mon opération basique x=4*I-1 par exemple.

alors vite fait j'ai remarqué mon erreur : j'ai mis un ":" par inadvertance au lieu d'un ";"
Le script tourne maintenant mais il me créé juste un fichier comprenant 777 lignes (donc 4*194+1 lignes) qui sont vierges.

il ne va donc pas chercher dans mon fichier.log les lognes voulues pour les imprimer dans le fichier.txt
je vais tenter 2/3 trucs en attendant des pistes, je vous tiens au courant si je trouve la solution en attendant votre aide.

merci d'avance ;) 

Voici comment j'ouvre un fichier en lecture dans un script :

  1. i=0
  2. for BUF in `cat $FICHIER`
  3. do
  4. if [condition] then action fi
  5. i=$i+1
  6. done


Tu accèdes à la ligne courante par $BUF. A toi de faire un test sur le numéro de ligne en cours ($i) et éventuellement de traiter ta ligne avec un cut ou autre pour récupérer ce que tu veux.

Merci pour cette piste, j'ai essayé de l'adapter ainsi mais j'ai toujours une erreur :

  1. i=1
  2. for BUF in `cat $erreqw1978.log`
  3. x=($i+1)/4
  4. do
  5. if let $x 2>/dev/null then print $BUF > eqw1978.txt fi
  6. i=$i+1
  7. done


l'erreur retournée est : Erreur de syntaxe près du symbole inattendu « x=($i+1)/4 »
j'ai essayé de rajouter un ";" comme sous Matlab mais ça n'a rien changé, une idée sur ce que j'ai raté ?

inverse ligne 3 et ligne 4 ... Le do doit forcément se trouver après le for.

Pour être plus propre créé une constante FICHIER (par exemple) et donne lui comme valeur le nom de ton fichier à analyser (et chemin complet si il n'est pas dans le répertoire du script)

mykhi a dit :
inverse ligne 3 et ligne 4 ... Le do doit forcément se trouver après le for.

Pour être plus propre créé une constante FICHIER (par exemple) et donne lui comme valeur le nom de ton fichier à analyser (et chemin complet si il n'est pas dans le répertoire du script)


ok merci, je viens de tenter ça donc :

  1. FICHIER='erreqw1978.log'
  2. i=1
  3. x=($i+1)/4
  4. for BUF in `cat $FICHIER`
  5. do
  6. if let $x 2>/dev/null then print $BUF > eqw1978.txt fi
  7. i=$i+1
  8. done


mais ça me renvoie toujours l'erreur, cette fois ci pour le "done" : Erreur de syntaxe près du symbole inattendu « done »

j'ai zappé un signe ?

PS : je viens de capter mais si je vire mon expression de x de la boucle ça va pas marcher ? il faut que je le mette dans la boucle for non si je veux qu'il évolue en fonction de i et pouvoir ainsi sélectionner les lignes ?

Si tu fais ce que tu as mis, x va prendre sa valeur une fois ((1+1)/4 en l'occurrence) et ne bougera plus. Je te conseille quelque chose du genre :

  1. FICHIER='erreqw1978.log'
  2. i=1
  3. for BUF in `cat $FICHIER`
  4. do
  5. x=($i+1)/4
  6. if let $x 2>/dev/null
  7. then
  8. print $BUF > eqw1978.txt
  9. fi
  10. i=$i+1
  11. done


Tant que tu ne rentres pas dans la structure du if, tu peux effectuer autant de commandes que tu le souhaites.

ok merci ! j'avais retenté en mettant l'expression mais après le do mais ça me retournait aussi la même erreur.

j'ai testé ce que tu m'as dit, ça a l'air de bien parcourir mon fichier mais ça fait des erreurs, voici un extrait de ce que ça me retourne :

  1. ....
  2. Error: no such file "11:51"
  3. Warning: unknown mime-type for "[sc_F02D002_910276278_atm_clean_dopcor.fits[*,1]]:" -- using "application/octet-stream"
  4. Error: no such file "[sc_F02D002_910276278_atm_clean_dopcor.fits[*,1]]:"
  5. Error: no such file "1976.975"
  6. Error: no such file "4.219E-18"
  7. Error: no such file "-9.05E-19"
  8. Error: no such file "0.2145"
  9. Error: no such file "-4.24E-19"
  10. Error: no such file "2.005"
  11. Warning: unknown mime-type for "0." -- using "application/octet-stream"
  12. Error: no such file "0."
  13. Warning: unknown mime-type for "(" -- using "application/octet-stream"
  14. Error: no such file "("
  15. Error: no such file "1.2548)"
  16. Warning: unknown mime-type for "(" -- using "application/octet-stream"
  17. Error: no such file "("
  18. Warning: unknown mime-type for ")" -- using "application/octet-stream"
  19. Error: no such file ")"
  20. Error: no such file "(1.1E-18)"
  21. Warning: unknown mime-type for "(" -- using "application/octet-stream"
  22. Error: no such file "("
  23. ....


apparemment si je comprends bien ça considère mes données comme des fichiers ?
merci de ton aide jusqu'à présent en tout cas ;) 

Meilleure solution

Bon désolé mon script initial n'était pas une bonne piste. Celà ne traite pas les lignes mais tout ce qui est séparé par un espace.

Voici un code awk pour n'afficher qu'une ligne sur 4 de ton fichier de log :

  1. FICHIER=erreqw1978.log
  2. awk 'NR%4==0{print $0}' $FICHIER

Rhooooolala.

Bon, d'abord, savoir si on veut une soluce en awk ou en shell.

Ensuite, si en awk on fait des trucs à la fin (END), on ne le fait qu'une fois, à la fin [:spamafote]
Re-étude :
  1. BEGIN {I=1; J=0}
  2. {print > "fichier_n"I".txt" ; J++ ; if (J%4==0) I++ }


  1. for BUF in `cat $FICHIER`
Que c'est pas beau !
En Bourne shell (bsh), c'est Ok. Mais depuis, il y a eu le Korn (ksh) puis le Bourne Again (bash). Alors on n'utilise plus la syntaxe anti-quotes `commande` mais la syntaxe dollar-parenthèses $( commande ). Le gros intérêt, en plus de vivre avec son temps, c'est que la syntaxe dollar-parenthèses est imbricable.

  1. for BUF in $( cat $FICHIER )
Si le fichier contient des lignes avec espaces, ça ne marche plus. Voici une astuce pour prendre toute la ligne :
  1. cat $FILE |
  2. while read LINE ; do
  3. echo "$LINE
  4. done


JP, respecte la syntaxe des commandes. Quand mykhi passe une ligne, fais-en autant, ou utilise un point-virgule :
  1. # // bon
  2. if ..
  3. then
  4. ...
  5. fi
  6.  
  7. while ..
  8. do
  9. ...
  10. done
  11.  
  12. # // pas bon
  13. if .. then
  14. ...
  15. fi
  16.  
  17. while .. do
  18. ...
  19. done
  20.  
  21. # // bon
  22. if .. ; then ... ; fi
  23. while .. ; do ... ; done

Ah, mykhi s'est repris :o  A la base c'est une bonne idée quand même ;) 
La soluce, c'est le cat .. | while read ..

zeb a dit :
Ah, mykhi s'est repris :o  A la base c'est une bonne idée quand même ;) 
La soluce, c'est le cat .. | while read ..


tu devais être en train de rédiger ton message quand j'ai posté le mien.

Oui mais bon ton message valait la peine d'attendre. Je ne suis qu'un amateur en shell. Je me débrouille pour mes besoins mais ce n'est certainement pas ce qui se fait de mieux en général.

Merci beaucoup à vous deux pour vos conseils !
pour la syntaxe effectivement je n'avais pas respecté les changements de ligne, etc... il faut que j'apprenne mieux ce langage.

j'ai testé ta version en 2 lignes mykhi et ça a bien marché (j'attends un peu que la discussion soit terminée avant de choisir la meilleure réponse mais je pense que je prendrai la tienne mykhi).
Par contre vu que je débute c'est un peu une boite noire pour moi, pourriez-vous m'aider à comprendre la première partie de cette ligne de commande ?

  1. awk 'NR%4==0{print $0}' $FICHIER > fichiertest.txt


que veulent dire donc le %, NR, et pourquoi le ==0, de même le print $0 ?
ça m'aiderait à comprendre pour pouvoir l'adapter ensuite et choisir à partir de quelle ligne je veux que ça me prenne une ligne sur 4 ou pour d'autres situations.

merci d'avance ;) 

NR : numéro de ligne courant lors de l'analyse
% : modulo
== 0 : teste l'égalité à 0 du modulo( si c'est un multiple de 4 (16 modulo 4 = 0, 17 modulo 4 = 1, ...)
$0 : ligne courante entière ($1 = 1er élément de la ligne, $2 = 2è élément de la ligne, ... selon un séparateur à définir)

d'accord merci pour ces précisions !
du coup j'ai donc changé la condition du modulo en 2 et 3 pour récupérer dans 2 autres fichiers les 2 autres lignes qui m'intéressent, ça a bien marché.
Il ne me reste plus ensuite qu'à filtrer puis concaténer...

sujet résolu donc, merci encore à vous deux ! ;) 

Rhoooo !

JPetrucci, quand je te propose d'étudier tel bout de code, j'entends que tu fasses l'effort de chercher toi-même, de tester, de lire la doc, etc. Si tu te contentes de poser la question icic, tu n'avanceras pas, ou très lentement. Et tu reviendras poser les mêmes questions. C'est dommage.

Sauf que ça fera des topics en plus, des pages vues, de la pub vue. Bref, BOM ne t'en voudra pas. ;) 

En parlant de faire un effort, tu veux bien mettre des balises [code] là ou tu as écris
Citation :
dans ton premier message. C'est juste pour faire joli.

Salut Zeb, concernant le bout de code que tu m'avais filé au début je l'ai regardé oui et j'ai essayé de comprendre comment il fonctionnait, peut-être pas aussi en profondeur que tu l'aurais voulu mais c'est vrai qu'en même temps "étudier" me paraissait vague et je ne voyais pas où vraiment tu voulais en venir, mais ça m'a quand même permis de comprendre que j'avais fait une erreur dans mon script initial au moins en oubliant de mettre des entrées dans les BEGIN et END, etc...

Après la doc bien évidemment je la lis mais quand tu as des pages de docs et que tu as des tas de petites fonctions comme awk, grep, etc.... c'est pas forcément évident de se contenter de la doc pour avancer, sur des docs aussi fournies on passe toujours à côté de choses on des fois on en interprète mal d'autres.

Puis si je ne faisais pas l'effort de chercher moi-même je n'aurais pas demandé plus de détails sur la construction des lignes qu'on m'a proposé ici, je me serais contenté de recopier bêtement sans comprendre, non ?
c'est justement en lisant les docs et les forums que j'ai pu tenter mon script initial, et c'est parce que je suis passé à côté de choses que j'ai posé ma question ici, pour en apprendre plus sur ce que j'utilisais mal ;) 

C'est juste. :) 

Tiens, j'ai fait un tuto de bonnes pratiques pour les utilisateurs du shell : http://www.presence-pc.com/forum/ppc/LeMondedeLinux/tut...

Il y a tant de petites commandes sous UNIX ! Mais c'est son grand avantage.

Les commandes à connaître : cat, grep, tail, head.
Celles à bien étudier : sed, awk.

Pour les autres commandes, il faut savoir à peu près à quoi elle servent et se référer à la manpage page au coup par coup.

A savoir que la commande awk est un véritable langage de programmation. Savoir bien l'utiliser est un avantage qui justifie qu'on se penche sur son étude. Certains ne s'embarrassent pas, et plutôt que d'utiliser le shell, préfère directement tout faire en perl.
Posez votre question