Cara melakukan multi line grep


15

Bagaimana Anda melakukan grep untuk teks yang muncul di dua baris?

Sebagai contoh:

pbsnodes adalah perintah yang saya gunakan yang mengembalikan pemanfaatan 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

Saya ingin menentukan jumlah procs yang cocok dengan node yang dalam status 'gratis'. Sejauh ini saya telah dapat menentukan "jumlah procs" dan "node dalam keadaan bebas", tetapi saya ingin menggabungkannya menjadi satu perintah yang menunjukkan semua procs gratis.

Dalam contoh di atas, jawaban yang benar adalah 6 (2 + 4).

Apa yang saya punya

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

Bagaimana saya bisa mencari setiap baris yang bertuliskan 'procs = x', tetapi hanya jika baris di atasnya berbunyi 'state = free?

Jawaban:


12

Jika data selalu dalam format itu, Anda bisa menuliskannya:

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

( RS=Berarti catatan adalah paragraf ).

Atau:

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

Inilah salah satu cara untuk melakukannya menggunakan pcregrep.

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

Contoh

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

3

Format output Anda disiapkan untuk slurp paragraf 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 }'

Catatan

Ini hanya berfungsi karena ide Perl tentang "paragraf" adalah bongkahan dari baris-baris yang tidak kosong yang dipisahkan oleh satu atau lebih baris-baris kosong. Jika Anda tidak memiliki garis kosong di antara nodebagian, ini tidak akan berhasil.

Lihat juga


3

Jika Anda memiliki data panjang tetap (panjang tetap mengacu pada jumlah baris dalam catatan), sedAnda dapat menggunakan Nperintah (beberapa kali), yang menggabungkan baris berikutnya ke ruang pola:

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

harus memberi Anda output seperti:

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

Untuk komposisi rekaman variabel (mis. Dengan garis pemisah kosong), Anda dapat menggunakan perintah percabangan tdan b, tetapi awkkemungkinan akan membawa Anda ke sana dengan cara yang lebih nyaman.


3

Implementasi GNU grepdilengkapi dengan dua argumen untuk juga mencetak baris sebelum ( -B) dan setelah ( -A) pertandingan. Cuplikan dari halaman manual:

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

Jadi dalam kasus Anda, Anda harus mencari state = freedan juga mencetak baris berikut. Jika digabungkan dengan cuplikan dari pertanyaan Anda, Anda akan menemukan sesuatu seperti itu:

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

dan sedikit lebih pendek:

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

awkapakah pencocokan pola; Anda tidak perlu grep: lihat jawaban Stephane
jasonwryan

Nah, sedapakah pencocokan pola juga. Anda juga dapat menggunakan perl, atau php, bahasa apa pun yang Anda inginkan. Tapi setidaknya judul pertanyaan yang diajukan untuk multi-line grep ... ;-)
binfalse

Yap: tetapi melihat Anda menggunakan awkanyways ... :)
jasonwryan

0

... dan berikut adalah solusi Perl:

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

0

Anda dapat menggunakan awk getlineperintah:

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

Dari 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.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.