Jak wykonać grep wieloliniowy


15

Jak wykonasz grep dla tekstu, który pojawia się w dwóch liniach?

Na przykład:

pbsnodes to polecenie, którego używam, które zwraca wykorzystanie klastra linuksowego

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

node2
    state = free
    procs = 4
    bar = foobar

node3
    state = busy
    procs = 8
    bar = foobar

Chcę określić liczbę procesów pasujących do węzłów, które są w stanie „wolne”. Do tej pory byłem w stanie określić „liczbę procesorów” i „węzły w stanie wolnym”, ale chcę je połączyć w jedno polecenie, które pokazuje wszystkie wolne procesy.

W powyższym przykładzie poprawna odpowiedź to 6 (2 + 4).

Co ja mam

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

Jak mogę wyszukać każdą linię, która brzmi „procs = x”, ale tylko wtedy, gdy linia nad nią brzmi „stan = wolny”?

Odpowiedzi:


12

Jeśli dane są zawsze w tym formacie, możesz po prostu je zapisać:

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

( RS=oznacza, że zapisy są akapitami ).

Lub:

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

Oto jeden ze sposobów, aby to zrobić za pomocą pcregrep.

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

Przykład

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

3

Twój format wyjściowy jest przygotowany na slurp akapitowy Perla:

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

Uwaga

Działa to tylko dlatego, że pomysł Perla na „akapit” jest kawałkiem niepustych linii oddzielonych jedną lub więcej pustymi liniami. Gdybyś nie miał pustych linii między nodesekcjami, to by nie zadziałało.

Zobacz też


3

Jeśli masz dane o stałej długości (stała długość odnosząca się do liczby linii w rekordzie), sedmożesz użyć Npolecenia (kilka razy), które łączy następny wiersz z przestrzenią wzorów:

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

powinien dać ci dane wyjściowe takie jak:

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

Dla zmiennym składzie (np rekord z pustą linią separatora), można skorzystać z poleceń rozgałęziających ti b, ale awkjest prawdopodobne, aby dostać się tam w bardziej wygodny sposób.


3

Implementacja GNU grepzawiera dwa argumenty do wydrukowania wierszy przed dopasowaniem ( -B) i po ( -A). Fragment ze strony podręcznika man:

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

Więc w twoim przypadku musisz grepować, state = freea także wydrukować następujący wiersz. Łącząc to z fragmentami twojego pytania, dojdziesz do czegoś takiego:

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

i nieco krótszy:

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

awkdopasowuje wzór; nie potrzebujesz grep: patrz odpowiedź Stephane'a
jasonwryan

Cóż, sedrównież dopasowuje wzór. Można również użyć perl, lub php, lub cokolwiek język wolisz. Ale przynajmniej nagłówek pytania
zadanego

Tak: ale widząc, że i awktak używasz ... :)
jasonwryan

0

... a oto rozwiązanie Perla:

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

0

Możesz użyć awk getlinepolecenia:

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

Od 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.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.