Comment effectuer un grep multi-lignes


15

Comment feriez-vous un grep pour du texte qui apparaît sur deux lignes?

Par exemple:

pbsnodes est une commande que j'utilise qui renvoie l'utilisation d'un cluster Linux

root$ pbsnodes
node1
    state = free
    procs = 2
    bar = foobar

node2
    state = free
    procs = 4
    bar = foobar

node3
    state = busy
    procs = 8
    bar = foobar

Je veux déterminer le nombre de procs qui correspondent aux noeuds qui sont à l'état «libre». Jusqu'à présent, j'ai pu déterminer le "nombre de procs" et "les nœuds à l'état libre", mais je veux les combiner en une seule commande qui montre tous les procs libres.

Dans l'exemple ci-dessus, la bonne réponse serait 6 (2 + 4).

Ce que j'ai

root$ NUMBEROFNODES=`pbsnodes|grep 'state = free'|wc -l`
root$ echo $NUMBEROFNODES
2

root$ NUMBEROFPROCS=`pbsnodes |grep "procs = "|awk  '{ print $3 }' | awk '{ sum+=$1 } END { print sum }'`
root$ echo $NUMBEROFPROCS
14

Comment puis-je rechercher chaque ligne qui lit «procs = x», mais uniquement si la ligne au-dessus indique «state = free»?

Réponses:


12

Si les données sont toujours dans ce format, vous pouvez simplement les écrire:

awk -vRS= '$4 == "free" {n+=$7}; END {print n}'

( RS=signifie que les enregistrements sont des paragraphes ).

Ou:

awk -vRS= '/state *= *free/ && match($0, "procs *=") {
  n += substr($0,RSTART+RLENGTH)}; END {print n}'

5
$ pbsnodes
node1
    state = free
    procs = 2
    bar = foobar

node2
    state = free
    procs = 4
    bar = foobar

node3
    state = busy
    procs = 8
    bar = foobar
$ pbsnodes | grep -A 1 free
    state = free
    procs = 2
--
    state = free
    procs = 4
$ pbsnodes | grep -A 1 free | grep procs | awk '{print $3}'
2
4
$ pbsnodes | grep -A 1 free | grep procs | awk '{print $3}' | paste -sd+ 
2+4
$ pbsnodes | grep -A 1 free | grep procs | awk '{print $3}' | paste -sd+ | bc 
6

https://en.wikipedia.org/wiki/Pipeline_(Unix)


4

Voici une façon de le faire en utilisant pcregrep.

$ pbsnodes | pcregrep -Mo 'state = free\n\s*procs = \K\d+'
2
4

Exemple

$ pbsnodes | \
    pcregrep -Mo 'state = free\n\s*procs = \K\d+' | \
    awk '{ sum+=$1 }; END { print sum }'
6

3

Votre format de sortie est amorcé pour le slurp de paragraphe de Perl:

pbsnodes|perl -n00le 'BEGIN{ $sum = 0 }
                 m{
                   state \s* = \s* free \s* \n 
                   procs \s* = \s* ([0-9]+)
                 }x 
                    and $sum += $1;
                 END{ print $sum }'

Remarque

Cela ne fonctionne que parce que l'idée de Perl d'un "paragraphe" est un bloc de lignes non vides séparées par une ou plusieurs lignes vides. Si vous n'aviez pas de lignes vides entre les nodesections, cela n'aurait pas fonctionné.

Voir également


3

Si vous avez des données de longueur fixe (longueur fixe faisant référence au nombre de lignes dans un enregistrement), sedvous pouvez utiliser la Ncommande (plusieurs fois), qui joint la ligne suivante à l'espace de motif:

sed -n '/^node/{N;N;N;s/\n */;/g;p;}'

devrait vous donner une sortie comme:

node1;state = free;procs = 2;bar = foobar
node2;state = free;procs = 4;bar = foobar
node3;state = busy;procs = 8;bar = foobar

Pour une composition d'enregistrement variable (par exemple avec une ligne de séparation vide), vous pouvez utiliser des commandes de branchement tet b, mais awkvous y arriverez probablement de manière plus confortable.


3

L'implémentation GNU de grepvient avec deux arguments pour afficher également les lignes avant ( -B) et après ( -A) une correspondance. Extrait de la page de manuel:

   -A NUM, --after-context=NUM
          Print NUM lines of trailing context after matching lines.  Places a line containing  a  group  separator  (--)  between  contiguous  groups  of  matches.   With  the  -o  or
          --only-matching option, this has no effect and a warning is given.

   -B NUM, --before-context=NUM
          Print  NUM  lines  of  leading  context  before  matching  lines.   Places  a  line  containing  a group separator (--) between contiguous groups of matches.  With the -o or
          --only-matching option, this has no effect and a warning is given.

Donc, dans votre cas, vous devrez rechercher state = freeet également imprimer la ligne suivante. En combinant cela avec les extraits de votre question, vous arriverez à quelque chose comme ça:

usr@srv % pbsnodes | grep -A 1 'state = free' | grep "procs = " | awk  '{ print $3 }' | awk '{ sum+=$1 } END { print sum }'
6

et un peu plus court:

usr@srv % pbsnodes | grep -A 1 'state = free' | awk '{ sum+=$3 } END { print sum }'
6

awkfait la correspondance des motifs; vous n'avez pas besoin grep: voir la réponse de Stéphane
jasonwryan

Eh bien, sedla correspondance des motifs est-elle également possible. Vous pouvez également utiliser perl, ou php, ou la langue que vous préférez. Mais au moins le titre de la question demandait une grep multi-lignes ... ;-)
binfalse

Oui, mais en voyant que vous utilisiez de awktoute façon ... :)
jasonwryan

0

... et voici une solution Perl:

pbsnodes | perl -lne 'if (/^\S+/) { $node = $& } elsif ( /state = free/ ) { print $node }'

0

Vous pouvez utiliser la awk getlinecommande:

$ pbsnodes | awk 'BEGIN { freeprocs = 0 } \
                  $1=="state" && $3=="free" { getline; freeprocs+=$3 } \
                  END { print freeprocs }'

De man awk :

   getline               Set $0 from next input record; set NF, NR, FNR.

   getline <file         Set $0 from next record of file; set NF.

   getline var           Set var from next input record; set NR, FNR.

   getline var <file     Set var from next record of file.

   command | getline [var]
                         Run command piping the output either into $0 or var, as above.

   command |& getline [var]
                         Run  command  as a co-process piping the output either into $0 or var, as above.  Co-processes are a
                         gawk extension.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.