intervalo grep de números em um arquivo de texto

0

Eu tenho o seguinte texto em um arquivo de texto

$ cat test
20180618:
20180619:
20180620:
20180621:
20180622:
20180623:
20180624:

Eu tentei usar o intervalo de números, como abaixo,

$ grep 201806{19..21} test
grep: 20180619: No such file or directory
grep: 20180620: No such file or directory
grep: 20180621: No such file or directory

Estou ficando acima do erro no ZSH e no bash. Parece grep tomando a string de pesquisa como arquivos.

Eu tentei de outra maneira:

$ grep 201806* test       
zsh: no matches found: 201806*

Estou recebendo esse erro apenas no ZSH. Qual é o caminho certo para usar o * no ZSH e como posso dizer ao grep para o intervalo de números do grep?

    
por smc 21.06.2018 / 12:07

4 respostas

1
grep 201806{19..21} test

é expandido pelo shell para:

grep 20180619 20180620 20180621 test

Qual grep entende como procurando 20180619 nos 3 arquivos, 20180620 , 20180621 e test .

Se você alterar para:

grep -e201806{19..21} test

Então, isso é expandido para:

grep -e20180619 -e20180620 -e20180621 test

Que fornece 3 e xpressions para grep para pesquisar em test .

Ou você pode fazer:

printf '%s\n' 201806{19..21} | grep -f - test

Onde passamos as expressões como um número de linhas de entrada para grep (com algumas implementações, você pode precisar de /dev/stdin no lugar de - ).

Com zsh especificamente, você também pode:

numbers=({19..21} 25 31)
grep -E "201801(${(j:|:)numbers})" test

Onde usamos o sinalizador de expansão do parâmetro (j:|:) para unir os elementos da matriz com | (o operador de alternância de expressão regular estendida) para que ele possa ser usado como um ERE.

Ou você poderia amarrar essa matriz a um escalar de expressão regular com:

$ typeset -T re numbers '|'
$ numbers=({19..21} 25 31)
$ echo $re
19|20|21|25|31

Embora as expressões regulares normalmente não tenham recursos de correspondência de intervalo de numeração, zsh patterns (que com extendedglob são funcionalmente equivalentes a expressões regulares) fazem com o operador <x-y> (somente para sequências de dígitos decimais):

print -rl -- ${(M)${(f)"$(<test)"}:#*201806<19-21>*}
    
por 22.06.2018 / 15:51
6

Sim, grep apenas trata seu argumento primeiro como uma expressão regular por padrão.

Isso significa que

grep {1..9} file

que se expande para

grep 1 2 3 4 5 6 7 8 9 file

invocaria grep com 1 como a expressão para corresponder nos outros operandos, e que esses outros operandos seriam nomes de arquivos.

Seu outro comando:

grep 201806* test

Isso tentaria combinar 201806* como um padrão de globalização de nome de arquivo. Você não tem arquivos cujos nomes iniciam com 201806 no diretório atual, portanto, o zsh shell não consegue expandir o padrão e fornece a mensagem de erro no matches found .

Em outros shells parecidos com Bourne, se o padrão não tivesse encontrado nenhum nome de arquivo, ele teria permanecido não expandido e usado como a expressão regular com grep . A expressão 201806* , quando considerada como uma expressão regular, corresponde a 20180 seguido por zero ou mais 6 caracteres, por ex. 2018066666 .

Em vez disso, convém construir uma expressão regular para corresponder ao seu intervalo:

grep -E '201806(19|20|21)' test

ou

grep -E '201806(19|2[01])' test

O -E é necessário para ter grep understand | (alternância) na expressão (essa alternação faz dela uma expressão regular estendida).

Você também pode criar uma expressão regular a partir de uma expansão de chave:

set -- {19..21}
re=$( IFS='|'; printf '201806(%s)' "$*" )

grep -E "$re" test

Isso definiria primeiro os parâmetros posicionais, $1 , $2 e $3 , para os números desejados no intervalo. A variável re seria então definida como 201806(%s) , em que printf substituiria %s por esses números delimitados por | .

A chamada grep usaria 201806(19|20|21) como expressão regular.

    
por 21.06.2018 / 12:14
2

strings sem aspas são interpretadas pelo shell antes do comando ser executado, no seu caso, o comando que você tentou seria expandido para grep 20180619 20180620 20180621 test

$ echo grep 201806{19..21} test
grep 20180619 20180620 20180621 test

Uma solução alternativa é especificar a alternância da expressão regular:

$ grep -E '201806(19|20|21)' test
20180619:
20180620:
20180621:

Você pode construir um intervalo numérico com expressões regulares, mas não é fácil. Consulte o link para obter detalhes


Outra opção é usar awk

$ awk -F: '$1>=20180619 && $1<=20180621' ip.txt
20180619:
20180620:
20180621:

Aqui, dividimos a linha em : e, em seguida, comparamos o primeiro campo $1 com o intervalo necessário

    
por 21.06.2018 / 12:14
1
  1. POSIX shell (sem bash ) com utils:

    seq 20180618 20180624 | grep -f - test
    
  2. numgrep :

    numgrep '/20180618..20180624/' < test
    
por 22.06.2018 / 19:08