Contar todas as linhas entre um delimitador

0

Eu quero contar o número de endereços anexados a cada dispositivo no exemplo abaixo. De preferência usando awk ou sed.

/dev/1
addr1 sometext sometext sometext
addr2 sometext sometext sometext
addr3 sometext sometext sometext
/dev/2
wwpn1 sometext sometext sometext
wwpn2 sometext sometext sometext
/dev/3
addr1 sometext sometext sometext
addr2 sometext sometext sometext
addr3 sometext sometext sometext
addr4 sometext sometext sometext
/dev/4
addr1 sometext sometext sometext

Abaixo, será exibida uma única correspondência, mas estou tendo problemas para incrementar isso para todos os dispositivos fora de apenas executar o comando manualmente várias vezes:

sed -n '/\/dev\/1/,/\/dev\/2/'p 
/dev/1 
addr1 sometext sometext sometext
addr2 sometext sometext sometext
addr3 sometext sometext sometext
/dev/2

O que eu estou procurando fazer seria iterar por todos os dispositivos, algo como:

for i in 'grep 'dev' somefile'; do sed -n '/$i/,/$insersecondmatchhere/'p ; done

O problema é que não tenho certeza de como obter a variável $ insertsecondmatchhere do loop for no início do comando.

    
por lollan 23.02.2016 / 14:05

3 respostas

2

Aqui está uma maneira simplista em awk ; se ele vir o padrão / dev / [0-9], copia qualquer padrão salvo & count e, em seguida, redefine o dispositivo & contagem; caso contrário, incrementa um contador. Depois de atingir o EOF, ele despeja o padrão salvo & contagem.

#!/bin/awk -f
{
  if ( /\/dev\/[0-9]/ ) {
    if (dev) { print dev, count; };
    dev=$0;
    count=0;
  } else {
    ++count;
  }
}
END {
  print dev, count;
}

e como você tem várias versões do awk e a solução do perl do terdon, aqui está uma solução feia do bash + grep + sed, já que essa parecia ser a direção que você estava indo inicialmente:

#!/usr/bin/env bash

declare -a devs
devs=( $(grep ^/dev/ input) )
for ((i=0; i < ${#devs[@]} - 1; i++)); do
  start=${devs[i]}
  end=${devs[i+1]}
  start=${start//\//\\/}
  end=${end//\//\\/}
  count=$(sed -n "/^$start/,/^$end/p" input | wc -l)
  count=$(( count - 2 ))
  echo for ${devs[i]}, count is $count
done
start=${devs[i]}
start=${start//\//\\/}
count=$(sed -n "/^$start/,\$p" input | wc -l)
count=$(( count - 1 ))
echo for ${devs[i]}, count is $count

O hack principal está escapando das barras invertidas nos nomes dos dispositivos antes de passá-los para sed .

    
por 23.02.2016 / 14:59
2

Algo parecido com isto?

awk -v RS="/dev/" 'NR!=1 {print "/dev/"$1":"NF-1}' file.txt
    
por 23.02.2016 / 14:48
0

Outra abordagem awk :

$ awk '{if(/^\//){n=$0;}else{l[n]++}}END{for(n in l){print n" : "l[n]}}' file 
/dev/1 : 3
/dev/2 : 2
/dev/3 : 4
/dev/4 : 1

O script awk definirá n para a linha atual se a linha atual começar com / . Se isso não acontecer, aumentará o valor de l[n] , ou seja, o valor armazenado na matriz associativa l para o valor atual de n . Isto é o que contará as linhas por seção. Depois que todo o arquivo tiver sido lido, no bloco END{} , imprimiremos cada n (nome da seção, as dev linhas) e o número de linhas nessa seção.

Você pode apostar isso em:

awk '{/^\//?n=$0:l[n]++;}END{for(n in l){print n" : "l[n]}}' file 

E a mesma ideia em (golfe) Perl:

$ perl -lne'/^\//?$n=$_:$l{$n}++;}{print"$_ : $l{$_}"for keys(%l)' file 
/dev/2 : 2
/dev/1 : 3
/dev/3 : 4
/dev/4 : 1

Que pode ser expandido para:

perl -lne 'if(/^\//){$n=$_}
           else{ $l{$n}++ } 
           END{print"$_ : $l{$_}" for keys(%l)}' file 
    
por 23.02.2016 / 15:06