So führen Sie einen mehrzeiligen Grep durch

15

Wie würden Sie ein Grep für Text ausführen, der in zwei Zeilen angezeigt wird?

Beispielsweise:

pbsnodes ist ein Befehl, den ich verwende und der die Auslastung eines Linux-Clusters zurückgibt

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

node2
    state = free
    procs = 4
    bar = foobar

node3
    state = busy
    procs = 8
    bar = foobar

Ich möchte die Anzahl der Prozesse ermitteln, die mit Knoten im Status "frei" übereinstimmen. Bisher konnte ich die "Anzahl der Procs" und "die Knoten im freien Zustand" bestimmen, aber ich möchte sie in einem Befehl kombinieren, der alle freien Procs anzeigt.

Im obigen Beispiel wäre die richtige Antwort 6 (2 + 4).

Was ich habe

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

Wie kann ich nach jeder Zeile suchen, die 'procs = x' lautet, aber nur, wenn die Zeile darüber 'state = free' lautet?

Spuder
quelle

Antworten:

12

Wenn die Daten immer in diesem Format vorliegen, können Sie sie einfach schreiben:

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

( RS=bedeutet, dass Datensätze Absätze sind ).

Oder:

awk -vRS= '/state *= *free/ && match($0, "procs *=") {
  n += substr($0,RSTART+RLENGTH)}; END {print n}'
Stéphane Chazelas
quelle
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)

apex_predator
quelle
4

Hier ist eine Möglichkeit, dies zu tun pcregrep.

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

Beispiel

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

Ihr Ausgabeformat ist für Perls Absatz-Slurp vorbereitet:

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 }'

Hinweis

Dies funktioniert nur, weil Perls Idee eines "Absatzes" ein Block von nicht leeren Zeilen ist, die durch eine oder mehrere leere Zeilen getrennt sind. Wenn Sie keine Leerzeilen zwischen den nodeAbschnitten hätten, hätte dies nicht funktioniert.

Siehe auch

Joseph R.
quelle
3

Wenn Sie Daten mit fester Länge haben (feste Länge, die sich auf die Anzahl der Zeilen in einem Datensatz bezieht), sedkönnen Sie den NBefehl (mehrmals) verwenden, mit dem die nächste Zeile mit dem Musterbereich verbunden wird:

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

sollte wie folgt aussehen:

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

Für die variable Satzzusammensetzung (z. B. mit einer leeren Trennlinie) könnten Sie Verzweigungsbefehle tund verwenden b, werden Sie jedoch awkwahrscheinlich bequemer dorthin gelangen.

peterph
quelle
3

Die GNU-Implementierung von grepkommt mit zwei Argumenten, um auch die Zeilen vor ( -B) und nach ( -A) einer Übereinstimmung zu drucken . Ausschnitt aus der Manpage:

   -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.

In Ihrem Fall müssten Sie also nach state = freeder folgenden Zeile suchen und diese auch ausdrucken. Wenn Sie das mit den Auszügen aus Ihrer Frage kombinieren, erhalten Sie Folgendes:

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

und etwas kürzer:

usr@srv % pbsnodes | grep -A 1 'state = free' | awk '{ sum+=$3 } END { print sum }'
6
binfalse
quelle
awkführt Pattern Matching durch; Sie brauchen nicht grep: Siehe Stephanes Antwort
Jasonwryan
Nun, sedauch Pattern Matching. Sie könnten auch perloder verwenden phpoder welche Sprache Sie auch immer bevorzugen. Aber zumindest die Überschrift der Frage fragte nach mehrzeiligem
Grep
Ja, aber Sie haben es awktrotzdem benutzt ... :)
jasonwryan
0

... und hier ist eine Perl-Lösung:

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

Sie können den awk getlineBefehl verwenden:

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

Von 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.
Skippy le Grand Gourou
quelle