web-dev-qa-db-ja.com

複数行のgrepを実行する方法

2行に表示されるテキストに対してgrepをどのように実行しますか?

例えば:

pbsnodesは、Linuxクラスターの使用率を返すコマンドです。

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

node2
    state = free
    procs = 4
    bar = foobar

node3
    state = busy
    procs = 8
    bar = foobar

「フリー」状態のノードに一致するプロシージャの数を確認したい。これまでのところ、「プロシージャの数」と「フリー状態のノード」を特定することができましたが、それらをすべてのフリープロシージャを表示する1つのコマンドに結合したいと考えています。

上記の例では、正解は6(2 + 4)になります。

私が持っているもの

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

'procs = x'と表示されているすべての行を検索するにはどうすればよいですか。ただし、その上の行が 'state = free'と表示されている場合のみですか?

15
spuder

データが常にその形式である場合は、単純にそれを書き込むことができます。

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

RS=レコードが段落であることを意味します)。

または:

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

5
apex_predator

pcregrepを使用してこれを行う1つの方法を次に示します。

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

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

GNU grepの実装には、一致の前(-B)と後(-A)の行も出力する2つの引数が付属しています。スニペット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.

したがって、あなたの場合、state = freeをgrepし、次の行も出力する必要があります。それを質問のスニペットと組み合わせると、次のようなものが得られます。

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

そして少し短い:

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

固定長データ(レコード内の行数を参照する固定長)がある場合は、sedNコマンドを(数回)使用して、次の行を結合できます。パターンスペース:

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

次のような出力が得られるはずです:

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

可変レコード構成(たとえば、空の区切り線を使用)では、分岐コマンドtおよびbを使用できますが、awkを使用すると、快適な方法。

3
peterph

あなたの出力フォーマットはPerlの段落Slurpのために準備されています:

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

これは、Perlの「段落」の概念が1つ以上の空白行で区切られた非空白行のチャンクであるためにのみ機能します。 nodeセクションの間に空白行がない場合、これは機能しませんでした。

も参照してください

3
Joseph R.

...そして、これがPerlソリューションです:

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

awkgetlineコマンドを使用できます:

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

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.