Lista de filtragem de números contendo dígitos sequenciais

2

Eu gostaria de filtrar muitos arquivos de texto. Cada arquivo contém uma longa lista de números. A filtragem dos arquivos deve ocorrer pelo número de dígitos consecutivos dentro de cada número.

Lista de exemplos de um dos arquivos:

Arquivo de entrada data.log :

12365
91738
349874
128152639
1234
7654
08767
1234567

Eu gostaria de:

1- filtre esta lista por números contendo dois dígitos consecutivos, a saída esperada deve ser:

12365
349874
128152639
1234
7654
08767
1234567

2- filtrar esta lista por números contendo três dígitos consecutivos, a saída esperada deve ser:

12365
349874
1234
7654
08767
1234567

3- filtre esta lista por números contendo quatro dígitos consecutivos, a saída esperada deve ser:

1234
7654
1234567

4- filtre esta lista por números contendo cinco dígitos consecutivos, a saída esperada deve ser:

1234567

Se a ordem dos dígitos consecutivos nos números for pequena a grande (por exemplo, 1234 ... etc) ou grande a pequena (por exemplo, 54321) deve ser incluída na saída.

    
por Zahi 22.09.2018 / 23:56

3 respostas

1

Usando grep , tee e rev , crie uma pequena função complicada cheia de bash -isms:

dqs() { a=${2:-123456789} ; [ "$1" -ge 2 ] &&  
        grep -iF "$(eval eval printf '%s\\n' \$\{a:\{0..$((${#a}-$1))\}:$1\} |
                    tee >(rev) )"
       }

Teste:

dqs 5 < data.log 
1234567
dqs 4 < data.log 
1234
7654
1234567
dqs 3 < data.log 
12365
349874
1234
7654
08767
1234567

Como funciona:

printf imprime uma lista de sequências do tamanho desejado, (como 123 , 234 , etc. ), tee acrescenta uma imagem espelho ( ou seja, da direita para a esquerda ou para trás) usando rev , então grep -f <(...) pesquisa a entrada padrão para qualquer item dessa lista.

Para fazer essa lista de seqüências, geralmente seria necessário um loop, ou seq , ou até mesmo ambos, mas aqui nós trapaceitamos usando bash expressão de sequência , combinada com um expansão da substring e algumas aritmética . Mas é impossível, porque o interpretador bash não pode executá-los na ordem desejada. Portanto, eval eval e vários% estratégicos\\ s são usados para forçar o bash a fazer as coisas na ordem correta.

O [ "$@" -gt 0 ] && não é funcionalmente necessário aqui, mas é mais seguro tê-lo. Ele garante que dqs tenha um e apenas um parâmetro numérico, ou grep não será executado. Isso impede que eval eval faça qualquer coisa mal .

Bônus: Adicionar um segundo argumento pode alterar o 123456789 para qualquer outra sequência e o código ainda deve funcionar. Por exemplo, dqs 4 123456789ABCDEF procuraria sequências hexadecimais de quatro dígitos (e sequências inversas) e dqs 3 $(printf %s {a..z}) procuraria sequências alfabéticas de três letras.

# search 'man bash' for the three most popular words 
# that have 3 three char alphabetic runs
man bash | tr ' ' '\n' | sort | uniq -c | sort -gr  | 
dqs 3 $(printf '%s' {a..z}) | head -3

Saída:

     92 first
     76 default
     38 environment
    
por 24.09.2018 / 03:30
0

Se você tiver muitos arquivos muito grandes, a regex correspondente no awk será lenta. Uma abordagem é aproveitar o grep para fazer o trabalho pesado, e o awk para construir a lista de strings para procurar (já que você não quer codificar isso). Ou seja,

$grep -E '12|98|23|87|34|76|45|65|56|54|67|43|78|32|89|21' data.log

Faz o truque para dois personagens, mas queremos poder fazer isso com até 9 caracteres. Você precisa -E para grep estendido para suportar a busca de múltiplos padrões (12 | 98 são dois padrões) - o grep simples não permite que você faça isso.

O awk pode fazer um loop sobre a string 123456789 retirando partes sucessivas, mas queremos ir para frente e para trás, então:

$awk 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<9;i++) print substr(f,i,2),substr(b,i,2)}'
12 98
23 87
34 76
45 65
56 54
67 43
78 32
89 21

Vamos adicionar algo para que o tamanho não seja codificado para dois (-vn = 3 define a variável n = 3 dentro do script awk):

$awk -vn=3 'BEGIN {f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123 987
234 876
345 765
456 654
567 543
678 432
789 321

E (quase lá!) obtenha o símbolo de pipe grep -E quer alterando o separador de registro de saída (ORS) e o separador de campo de saída (OFS) para |

$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}'
123|987|234|876|345|765|456|654|567|543|678|432|789|321|

Nós temos que nos livrar do pipe final depois de 321, ou grep irá combinar tudo, então adicione sed '. $ //' para substituir o último caractere antes do final da string ($) com nada:

$awk -vn=3 'BEGIN {ORS="|" ; OFS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'

E agora podemos juntar tudo em um script de shell que nos permite fazer a pesquisa em geral:

$cat t.sh
#!/bin/bash
grep -E 'awk --assign n=$1 'BEGIN {OFS="|" ; ORS="|" ; f=123456789 ; b=987654321 ; for(i=1;i<11-n;i++) print substr(f,i,n),substr(b,i,n)}' | sed 's/.$//'' $2

$chmod 775 t.sh
$./t.sh 4 data.log
1234
7654
1234567
    
por 25.09.2018 / 00:46
-1

Muitos arquivos grandes indicam que isso precisa ser feito rapidamente. Isso significa que um loop while read está fora de questão . Uma coisa a perceber aqui é que cada um desses exercícios pode ser simplificado para combinar (pelo menos) um de um pequeno conjunto de padrões, e isso pode ser feito realmente rápido usando grep ou ferramentas similares como rg ou ack . Por exemplo, para sequências de cinco dígitos:

grep -e 12345 -e 23456 […] -e 65432 -e 54321

Veja man grep para mais informações, e use o Wiki do Greg para aprender Bash rapidamente.

    
por 23.09.2018 / 00:02