IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Shell et commandes GNU Discussion :

[awk] Récupérer le nombre de ligne total de la variable NR


Sujet :

Shell et commandes GNU

  1. #1
    Membre du Club
    Profil pro
    ingénieur
    Inscrit en
    Septembre 2008
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Septembre 2008
    Messages : 54
    Points : 57
    Points
    57
    Par défaut [awk] Récupérer le nombre de ligne total de la variable NR
    Bonjour,

    je me casse la tête depuis ce matin sur un truc qui je pense est simple mais dont j'ai beau retourné dans tout les sens je n'arrive pas à avoir le résultat que je veux.

    En gros voici mon problème, j'ai un fichier comme l'/etc/passwd sous linux dont je souhaite afficher toute les ligne sauf la dernière, je sais c'est basique et il y a de multiple façon de le faire simplement mais si je suis ici c'est parce que je veux le faire avec une certaine manière

    Je voudrais donc pourvoir faire cette opération en utilisant la variable NR de awk, du style :

    1) Connaitre le nombre de ligne avec NR
    2) Faire une boucle du style "tant que i < NR; afficher la ligne; ++i"

    Le problème est que NR n'affiche le total que si on lui met un END à la fin, donc si je fais ma boucle ça va afficher plusieurs fois la même chose à chaque énumération de la la ligne. Par conséquent est-il possible de récupérer le total de la variable NR dès le début et de l'utiliser comme nombre fixe pour faire la boucle ?

    Concrètement j'ai un fichier toto de ce style :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ligne1
    ligne2
    ligne3
    ligne4
    ligne5
    si je fais une boucle ça donne ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # cat /tmp/plik |awk  '{i=0; while (i < NR) {print; i++;}}'
    ligne1
    ligne2
    ligne2
    ligne3
    ligne3
    ligne3
    ligne4
    ligne4
    ligne4
    ligne4
    ligne5
    ligne5
    ligne5
    ligne5
    ligne5
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    # cat /tmp/plik |awk  '{print NR}'
    1
    2
    3
    4
    5
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    # cat /tmp/plik |awk  'END {print NR}'
    5
    ce que je voudrais c'est ça:

    Code pseudo-code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #<super awk de la mort qui tue> NR ?
    ligne1
    ligne2
    ligne3
    ligne4


    Je ne sais pas si c'est faisable ?

    Merci d'avance.

  2. #2
    Membre éprouvé Avatar de balkany
    Homme Profil pro
    Touriste
    Inscrit en
    Juillet 2017
    Messages
    346
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Touriste

    Informations forums :
    Inscription : Juillet 2017
    Messages : 346
    Points : 977
    Points
    977
    Par défaut
    Si vraiment tu as besoin de faire une boucle, alors tu remplis un tableau avec le contenu de ton fichier, et arrivé à END, tu travailles avec (et avec NR ayant la bonne valeur) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk '{a[NR]=$0} END{for (i=1;i<NR;i++) print a[i]}' fichier
    S'il s'agit juste d'afficher toutes les lignes sauf la dernière, tu peux faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk 'NR>1{print line} {line=$0}' fichier

  3. #3
    Expert confirmé
    Avatar de becket
    Profil pro
    Informaticien multitâches
    Inscrit en
    Février 2005
    Messages
    2 854
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Informaticien multitâches
    Secteur : Service public

    Informations forums :
    Inscription : Février 2005
    Messages : 2 854
    Points : 5 915
    Points
    5 915
    Par défaut
    Salut,

    Un truc comme ceci peut être :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     awk ' BEGIN { getline test < "/etc/passwd" }  { if ( getline test < "/etc/passwd" != 0 ) { print  $0   }   }' /etc/passwd

  4. #4
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 278
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 278
    Points : 12 726
    Points
    12 726
    Par défaut
    Une méthode rustique pour afficher toutes les lignes sauf la dernière (fonctionne au moins en gawk) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk '{p=c ; c=$0} $0=p' fichier
    Cordialement.

  5. #5
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 458
    Points
    13 458
    Par défaut
    Bonjour

    D'abord, si quelqu'un passe par ici et se demande, la façon simple de faire est celle-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk '{p=c ; c=$0} $0=p' fichier
    Un script qui a la vertu de supprimer les lignes vides par la même occasion .
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  6. #6
    Membre du Club
    Profil pro
    ingénieur
    Inscrit en
    Septembre 2008
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Septembre 2008
    Messages : 54
    Points : 57
    Points
    57
    Par défaut
    Merci à tous pour vos réponses, la méthode de Balakany correspond exactement à ce que je voulais

    Par contre j'aimerais comprendre comment ce que la commande fait exactement ?

    Celui-ci j'ai compris et je le trouve très élégant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk '{a[NR]=$0} END{for (i=1;i<NR;i++) print a[i]}' fichier
    Par contre celui-ci malgré la simplicité de sa syntaxe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk 'NR>1{print line} {line=$0}' fichier
    De ce que je comprends awk lit le fichier, énumère chaque lignes lues mais au lieu de commencer par la ligne 1 et il commence par la ligne 2, ensuite il les affiche, mais je comprends pas comment il substitue dans l'exemple ci-dessous la ligne2 avec la ligne1 ? Comment ce décalage a pu se faire ? c'est une sorte de "shift" comme en shell ?

    # cat /tmp/plik  |awk 'NR>1 {print NR, $0}'
    2 ligne2
    3 ligne3
    4 ligne4
    5 ligne5
    # cat /tmp/plik  |awk 'NR>1 {print NR, ligne}{ligne=$0}'
    2 ligne1
    3 ligne2
    4 ligne3
    5 ligne4

  7. #7
    Expert éminent sénior Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 243
    Points : 13 458
    Points
    13 458
    Par défaut
    La variable garde la totalité de la ligne en mémoire. Quand on est à la ligne n on traite, donc affiche, la ligne n-1. Pour afficher la dernière ligne, il faudrait être à la ligne FIN + 1. Ce qui est évidemment impossible. Et pour que aucune action ne soit effectuée à la première ligne, on met une condition NR>1. Le côté strict de l'inégalité est important. On aurait pu mettre NR!=1.
    Cette réponse vous apporte quelque chose ? Cliquez sur en bas à droite du message.

  8. #8
    Expert éminent sénior Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 278
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 278
    Points : 12 726
    Points
    12 726
    Par défaut
    Plaisanterie à part, un détail à savoir si on utilise la méthode n° 2 de Balkany (la plus viable si on utilise awk) :

    Ne faut-il pas mieux choisir FNR au lieu de NR ?

    Ça dépend du besoin. Si on veut afficher la totalité des lignes de plusieurs fichiers sauf la dernière de chaque fichier, on utilisera FNR, si on veut afficher la totalité de toutes les lignes de plusieurs fichiers sauf la dernière du dernier fichier, on utilisera NR.
    Cordialement.

  9. #9
    Modérateur
    Avatar de N_BaH
    Profil pro
    Inscrit en
    Février 2008
    Messages
    7 550
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2008
    Messages : 7 550
    Points : 19 383
    Points
    19 383
    Par défaut
    ce n'est jamais une bonne idée de faire des tests de programmation en tant que root.
    .
    N'oubliez pas de consulter les cours shell, la FAQ, et les pages man.

  10. #10
    Membre du Club
    Profil pro
    ingénieur
    Inscrit en
    Septembre 2008
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Septembre 2008
    Messages : 54
    Points : 57
    Points
    57
    Par défaut
    Merci pour les précisions, en faite je ne savais pas que l'on pouvait initialiser des variables en faisant juste comme ça :

    Je pensais que l'on était obligé de le définir obligatoirement comme NOM_VARIABLE=TOTO, la on le défini sans NOM et obligatoirement avec un print d'après ce que je comprends ? Je vois une différence selon la position du print

    fichier d'origine :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    cat /tmp/plik
    ligne1
    ligne2
    ligne3
    ligne4
    ligne5
    ensuite avec awk :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    cat /tmp/plik  |awk 'NR>1 {ligne}{print line=$0}'
    ligne1
    ligne2
    ligne3
    ligne4
    ligne5
    cat /tmp/plik  |awk 'NR>1 {print ligne}{line=$0}'
    ligne1
    ligne2
    ligne3
    ligne4
    La syntaxe permet donc d'initialiser une variable qui va s'appeler line ==> Qui ensuite va lire la ligne courante et l'enregistrer ? ==> Le "print" va servir à lire ?

    J'ai essayé de l'initialiser dès le départ avec BEGIN ou avec l'option -v mais je n'ai pas le même résultat, d'où viendrait la confusion ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    cat /tmp/plik  | awk -v ligne=$0 'NR>1  {print $ligne}'
    ligne2
    ligne3
    ligne4
    ligne5
    cat /tmp/plik  |awk 'BEGIN {ligne=$0}; NR>1  {print $ligne}'
    ligne2
    ligne3
    ligne4
    ligne5
     cat /tmp/plik  |awk 'NR>1 {print ligne}{ligne=$0}'
    ligne1
    ligne2
    ligne3
    ligne4

  11. #11
    Membre éprouvé Avatar de balkany
    Homme Profil pro
    Touriste
    Inscrit en
    Juillet 2017
    Messages
    346
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Touriste

    Informations forums :
    Inscription : Juillet 2017
    Messages : 346
    Points : 977
    Points
    977
    Par défaut
    Citation Envoyé par sedawk Voir le message
    d'où viendrait la confusion ?
    Les confusions sont multiples en l'occurrence…
    Tout d'abord, le découpage que tu fais du programme awk n'est pas valide : un programme awk, comme te l'apprendra le manuel de awk, est une suite de couples condition - action.
    Si une condition est vide, on exécute par défaut l'action pour toutes les lignes ; si une action est vide, on affiche par défaut la ligne courante.
    Donc le programme awk se découpe comme suit : premier couple condition - action :
    Si le numéro de la ligne courante est strictement supérieur à 1, on affiche le contenu de la variable line (sans avoir besoin de s'interroger sur son contenu à ce stade).
    Deuxième couple condition - action :
    Ici, il n'y a pas de condition, donc pour chaque ligne, on met la ligne courante dans la variable line.
    Le résultat logique de tout cela, c'est qu'à la première ligne, on n'affiche rien, mais on met cette première ligne dans la variable line.
    À la deuxième ligne, on affiche le contenu de la variable line, donc la ligne 1, et on met la deuxième ligne dans la variable line.
    À la troisième ligne, on affiche le contenu de la variable line, donc la ligne 2, et on met la troisième ligne dans la variable line.
    Ainsi de suite jusqu'à la fin du fichier.

    Pour le reste, désolé d'être un peu abrupt, mais la plupart des choses que tu as écrites n'ont aucun sens.
    Reprends les une à une, vois pourquoi elles n'ont pas de sens, et ça te fera avancer (en particulier, qui est $0 ? quand cette expression a-t-elle un sens ? quand a-t-elle un contenu ?)
    Fais aussi preuve de plus de rigueur, en particulier pour le nommage de tes variables : ligne et line sont deux choses différentes, aussi bien pour l'ordinateur que pour les humains qui te lisent, et qui sont obligés de corriger à la volée ce que tu écrit pour essayer de te comprendre.

  12. #12
    Membre du Club
    Profil pro
    ingénieur
    Inscrit en
    Septembre 2008
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Septembre 2008
    Messages : 54
    Points : 57
    Points
    57
    Par défaut
    Merci Balkany pour tes explications claires et oui tu as raisons je me suis bien éparpillé dans ma tentative de compréhension du code, à noter que le fait que je n'ai pas été très rigoureux dans ma syntaxe ne m'a pas aidé beaucoup non plus

    C'est un mal pour bien car cela m'a fait énormément progressé sur la compréhension de mon problème qui à l'air compliqué de prime à bord mais qui au final requérait du bon sens Donc pour illustrer ma compréhension de ma commande ci-joint un petit schéma sur le déroulement de l'éxécution de la commande :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    Fichier de données :
    
    cat /tmp/plik :
    
    ligne1
    ligne2
    ligne3
    ligne4
    ligne5
    
    Paramètres :
    
    NR total = 5 Lignes
    
    (var) ligne = variable ou sera enregistrée la ligne courante
    
    
    Commande à passer pour obtenir toutes les lignes sauf la dernière :
    
    cat /tmp/plik  |awk 'NR>1 {print ligne}{ligne=$0}'
    
    ligne1
    ligne2
    ligne3
    ligne4
    
    Déroulement des actions :
    
    Si NR > 1 on affiche la variable ligne ET on enregistre la ligne courante $0 dans la variable "ligne"
    
    ligne1 
    
    CONDITION : NR = 1 ==>NR>1 ? NON  ACTION ==> RIEN + ACTION ==> (var) ligne=ligne1
    
    ligne2
    
    CONDITION : NR = 2 ==> NR>1 ? OUI  ACTION ==> print (var) ligne=ligne1 + ACTION ==> (var) ligne=ligne2
    
    ligne3
    
    CONDITION : NR = 3 ==>NR>1 ? OUI  ACTION ==> print (var) ligne=ligne2 + ACTION ==> (var) ligne=ligne3
    
    ligne4
    
    CONDITION : NR = 4 ==> NR>1 ? OUI  ACTION ==> print (var) ligne=ligne3 + ACTION ==> (var) ligne=ligne4
    
    ligne5
    
    CONDITION : NR = 5 ==>NR>1 ? OUI  ACTION ==> print (var) ligne=ligne4 + ACTION ==> (var) ligne=ligne5
    
    
    NR Total = 5 donc plus de lignes à traiter, donc fin du programme.
    
    Ce qui peut se vérifier avec un END à la fin du programme :
    
    cat /tmp/plik  |awk 'NR>1 {print ligne}{ligne=$0} END {print "ET POUR FINIR LE CONTENU DE LA VARIABLE A LA FIN : " ligne}'
    
    ligne1
    ligne2
    ligne3
    ligne4
    ET POUR FINIR LE CONTENU DE LA VARIABLE A LA FIN : ligne5

  13. #13
    Membre éprouvé Avatar de balkany
    Homme Profil pro
    Touriste
    Inscrit en
    Juillet 2017
    Messages
    346
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Touriste

    Informations forums :
    Inscription : Juillet 2017
    Messages : 346
    Points : 977
    Points
    977
    Par défaut
    Impeccable
    Juste une remarque : tu n'as pas besoin de faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cat fichier | awk 'programme-awk'
    Il suffit de faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    awk 'programme-awk' fichier
    Cela se généralise à toute commande qui lit depuis l'entrée standard : soit elle accepte une syntaxe où figure directement le fichier d'entrée, comme pour awk ci-dessus, soit on utilise une redirection :
    Cela évite un appel inutile à la commande cat.
    Ce type d'optimisation te sera proposé par le programme shellcheck, exécutable en ligne : https://www.shellcheck.net/, et que tu peux aussi installer chez toi.
    Il te renverra alors vers des pages de son wiki pour explication, et dans le cas présent à celle-ci : https://github.com/koalaman/shellcheck/wiki/SC2002

  14. #14
    Membre du Club
    Profil pro
    ingénieur
    Inscrit en
    Septembre 2008
    Messages
    54
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur

    Informations forums :
    Inscription : Septembre 2008
    Messages : 54
    Points : 57
    Points
    57
    Par défaut
    Merci pour les lien !

  15. #15
    Expert confirmé
    Homme Profil pro
    Développeur informatique en retraite
    Inscrit en
    Avril 2008
    Messages
    2 101
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côtes d'Armor (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique en retraite

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 101
    Points : 5 849
    Points
    5 849
    Par défaut
    Citation Envoyé par balkany Voir le message
    premier couple condition - action :
    Si le numéro de la ligne courante est strictement supérieur à 1, on affiche le contenu de la variable line (sans avoir besoin de s'interroger sur son contenu à ce stade).
    Deuxième couple condition - action :
    Ici, il n'y a pas de condition, donc pour chaque ligne, on met la ligne courante dans la variable line.
    Le résultat logique de tout cela, c'est qu'à la première ligne, on n'affiche rien, mais on met cette première ligne dans la variable line.
    À la deuxième ligne, on affiche le contenu de la variable line, donc la ligne 1, et on met la deuxième ligne dans la variable line.
    À la troisième ligne, on affiche le contenu de la variable line, donc la ligne 2, et on met la troisième ligne dans la variable line.
    Ainsi de suite jusqu'à la fin du fichier.
    Je suis complètement d'accord avec Balkany.

    En fait, l'astuce et la confusion viennent de ce que la variable line contient parfois la ligne courante et parfois la ligne précédente! (suivant le point de vue où on se place)
    Dans la partie {line=$0}, on enregistre le contenu de la ligne courante dans la variable line.
    Dans la partie NR>1{print line}, on imprime le contenu de la variable line qui contient... la ligne précédente, puisque la variable a été affectée au tour d'avant.
    Évidemment, l'ordre des couples condition - action est primordial.
    Si on faisait {line=$0} NR>1{print line}, on imprimerait toutes les lignes sauf la première, car la variable line contiendrait toujours la ligne courante.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Récupérer le nombre de lignes totales d'une datatable
    Par Jordan-Tess dans le forum jQuery
    Réponses: 6
    Dernier message: 27/11/2014, 14h27
  2. Réponses: 2
    Dernier message: 20/03/2007, 13h39
  3. [MySQL] Récupérer le nombre de lignes
    Par popogendarme dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 31/01/2007, 15h20
  4. Réponses: 3
    Dernier message: 27/09/2006, 11h35
  5. [javascript/dom] Récupérer le nombre de lignes d'un tableau
    Par LE NEINDRE dans le forum Général JavaScript
    Réponses: 6
    Dernier message: 17/02/2006, 11h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo