Verifique alguma propriedade para cada linha de um arquivo

1

Eu tenho vários (427 para ser preciso) arquivos de texto com um milhão de linhas, cada um contendo 31 números separados por espaços (possíveis espaços duplos). No entanto, pode haver alguma corrupção de dados e pode haver linhas contendo lixo.

Agora quero verificar se cada linha satisfaz a propriedade de conter 31 itens separados por espaços (suponho que esses itens sejam números. Um método que verifique isso também seria melhor).

Meu jeito atual é

while read line;
do
   if [ $(echo "$line" | sed 's/ /\n/g' | grep -v "^$" | wc -l) -ne 31 ]
   then
      echo "$file bad";
   fi
done < $file

Isso substitui todos os espaços de uma linha por novas linhas, filtra as linhas vazias, conta o número de linhas e as compara com 31.

Essa abordagem é lenta e provavelmente há uma maneira muito melhor de envolver alguma expressão regular sofisticada. Qual seria o caminho mais rápido?

    
por stefan 27.03.2013 / 16:06

3 respostas

4

Faça simplesmente:

awk 'NF != 31 || /[^0-9 -]/ {print FILENAME ":" FNR ": " $0}' file1 file2...

Para relatar as linhas que não possuem 31 campos ou não têm dígitos. Não tão estrito quanto a solução do @ manatwork, já que ele não iria latir em --- ou 9-8 , por exemplo, mas pode ser mais eficiente.

    
por 27.03.2013 / 16:21
6

Por que não apenas grep sozinho?

bash-4.2$ cat file
1 2 -3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
32 33 -34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 L 51 52 53 54 55 56 57 58 59 60 61 62
63 64 -65 66 67 68 69
70 71 -72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100

# listing bad lines in the file
bash-4.2$ grep -Exv '(-?[[:digit:]]+ +){30}-?[[:digit:]]+' file
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 L 51 52 53 54 55 56 57 58 59 60 61 62
63 64 65 66 67 68 69

# listing files with bad lines
bash-4.2$ grep -Exvl '(-?[[:digit:]]+ +){30}-?[[:digit:]]+' -- *
file
    
por 27.03.2013 / 16:15
1

Você pode ler a linha em uma matriz usando read -a e, em seguida, verificar o tamanho da matriz. Isso deve ser consideravelmente melhor do que gerar um subshell para separar 3 processos de cada linha.

while read -ra line;
do
    if (( ${#line[@]} != 31 )); then
        echo "$file bad"
    fi
done < "$file"
    
por 27.03.2013 / 16:14