Estou aprendendo Linux e tenho um desafio que parece não conseguir resolver sozinho. Aqui está:
grep uma linha de um arquivo que contém 4 números seguidos, mas não mais que 4.
Não tenho certeza de como abordar isso. Posso procurar números específicos, mas não o valor em uma sequência.
1234a12345
ser exibida ou não?\b\d{4}\b
Respostas:
Existem duas maneiras de interpretar essa pergunta; Vou abordar os dois casos. Você pode querer exibir linhas:
Por exemplo, (1) seria exibido
1234a56789
, mas (2) não.Se você deseja exibir todas as linhas que contêm uma sequência de quatro dígitos que não faz parte de nenhuma sequência mais longa de dígitos, uma maneira é:
Isso usa expressões regulares do Perl , suportadas pelo Ubuntu
grep
( GNU grep )-P
. Não corresponderá a texto como12345
, nem corresponderá ao1234
ou2345
que faz parte dele. Mas vai combinar com o1234
in1234a56789
.Nas expressões regulares do Perl:
\d
significa qualquer dígito (é uma maneira curta de dizer[0-9]
ou[[:digit:]]
).x{4}
correspondex
4 vezes. (A{
}
sintaxe não é específica para expressões regulares do Perl; também está em expressões regulares estendidasgrep -E
.) Assim\d{4}
é o mesmo que\d\d\d\d
.(?<!\d)
é uma asserção negativa de largura zero. Significa "a menos que seja precedido por\d
".(?!\d)
é uma afirmação de antecipação negativa de largura zero. Significa "a menos que seja seguido por\d
".(?<!\d)
e(?!\d)
não corresponde ao texto fora da sequência de quatro dígitos; em vez disso, eles (quando usados juntos) impedirão que uma sequência de quatro dígitos seja correspondida se fizer parte de uma sequência mais longa de dígitos.Usar apenas o look-behind ou apenas o look-ahead é insuficiente porque a subsequência de quatro dígitos mais à direita ou à esquerda ainda seria correspondida.
Um benefício do uso de asserções look-behind e look-ahead é que seu padrão corresponde apenas às seqüências de quatro dígitos, e não ao texto ao redor. Isso é útil ao usar o destaque de cores (com a
--color
opção).Por padrão no Ubuntu, cada usuário tem
alias grep='grep --color=auto'
em seu~.bashrc
arquivo . Assim, você obtém o realce de cores automaticamente quando executa um comando simples começando comgrep
(isto é, quando os aliases são expandidos) e a saída padrão é um terminal (é isso que verifica). As correspondências geralmente são destacadas em um tom de vermelho (próximo ao vermelhão ), mas eu o mostrei em negrito em itálico. Aqui está uma captura de tela:--color=auto
E você pode até
grep
imprimir apenas o texto correspondente, e não a linha inteira, com-o
:Maneira Alternativa, Sem Declarações de Look-Behind e Look-Ahead
No entanto, se você:
grep
não suporta-P
ou de outra forma não deseja usar uma expressão regular Perl, e... então você pode conseguir isso com uma expressão regular estendida :
Combina quatro dígitos e o caractere que não é um dígito - ou o início ou o fim da linha - ao redor deles. Especificamente:
[0-9]
corresponde a qualquer dígito (como[[:digit:]]
, ou\d
em Perl expressões regulares) e{4}
significa "quatro vezes." Então[0-9]{4}
corresponde a uma sequência de quatro dígitos.[^0-9]
corresponde caracteres não na faixa de0
meio9
. É equivalente a[^[:digit:]]
(ou\D
, em expressões regulares do Perl).^
, quando não aparece[
]
entre colchetes, corresponde ao início de uma linha. Da mesma forma,$
corresponde ao final de uma linha.|
meios ou e parênteses são para agrupar (como na álgebra). Portanto,(^|[^0-9])
corresponde ao início da linha ou a um caractere que não é dígito, enquanto($|[^0-9])
corresponde ao final da linha ou a um caractere que não é dígito.Portanto, as correspondências ocorrem apenas em linhas que contêm uma sequência de quatro dígitos (
[0-9]{4}
) que é simultaneamente:(^|[^0-9])
), e($|[^0-9])
).Se, por outro lado, você deseja exibir todas as linhas que contêm uma sequência de quatro dígitos, mas não contém nenhuma sequência com mais de quatro dígitos (mesmo uma que esteja separada de outra sequência de apenas quatro dígitos), então conceitualmente O objetivo é encontrar linhas que correspondam a um padrão, mas não a outro.
Portanto, mesmo que você saiba como fazê-lo com um único padrão, sugiro usar algo como a segunda sugestão de matt ,
grep
para os dois padrões separadamente.Você não se beneficia fortemente de nenhum dos recursos avançados das expressões regulares do Perl ao fazer isso; portanto, pode preferir não usá-los. Mas, de acordo com o estilo acima, aqui está uma redução da solução de matt usando
\d
(e chaves) no lugar de[0-9]
:Como usa
[0-9]
, o modo de matt é mais portátil - ele funcionará em sistemas ondegrep
não suporta expressões regulares do Perl. Se você usar[0-9]
(ou[[:digit:]]
) em vez de\d
, mas continuar usando{
}
, você obtém a portabilidade do modo matt de forma um pouco mais concisa:Maneira alternativa, com um único padrão
Se você realmente prefere um
grep
comando quegrep
s separados por um tubo , como acima)... então você pode usar:
A
-x
sinalizaçãogrep
exibe apenas as linhas onde a linha inteira corresponde (em vez de qualquer linha que contenha uma correspondência).Eu usei uma expressão regular Perl porque acho que a brevidade
\d
e\D
aumente substancialmente a clareza nesse caso. Mas se você precisar de algo portátil para sistemas ondegrep
não suporta-P
, poderá substituí-los por[0-9]
e[^0-9]
(ou por[[:digit:]]
e[^[:digit]]
):A maneira como essas expressões regulares funcionam é:
No meio,
\d{4}
ou[0-9]{4}
corresponde a uma sequência de quatro dígitos. Podemos ter mais de um deles, mas precisamos ter pelo menos um.À esquerda,
(\d{0,4}\D)*
ou([0-9]{0,4}[^0-9])*
corresponde a zero ou mais (*
) instâncias de não mais que quatro dígitos seguidos por um não dígito. Zero dígitos (ou seja, nada) é uma possibilidade para "não mais que quatro dígitos". Isso corresponde (a) à string vazia ou (b) a qualquer string que termine com um não dígito e não contenha nenhuma sequência com mais de quatro dígitos.Como o texto imediatamente à esquerda da central
\d{4}
(ou[0-9]{4}
) deve estar vazio ou terminar com um não dígito, isso impede que a central\d{4}
corresponda a quatro dígitos que possuem outro (quinto) dígito à esquerda deles.À direita,
(\D\d{0,4})*
ou([^0-9][0-9]{0,4})*
corresponde a zero ou mais (*
) instâncias de um não dígito seguido por não mais de quatro dígitos (que, como antes, poderiam ser quatro, três, dois, um ou até mesmo nenhum). Isso corresponde (a) à sequência vazia ou (b) a qualquer sequência iniciada em um dígito e que não contenha nenhuma sequência de mais de quatro dígitos.Como o texto imediatamente à direita da central
\d{4}
(ou[0-9]{4}
) deve estar vazio ou começar com um não-dígito, isso impede que a central\d{4}
corresponda a quatro dígitos que possuem outro (quinto) dígito à direita deles.Isso garante que uma sequência de quatro dígitos esteja presente em algum lugar e que nenhuma sequência de cinco ou mais dígitos esteja presente em qualquer lugar.
Não é ruim ou errado fazê-lo dessa maneira. Mas talvez o motivo mais importante para considerar essa alternativa seja o fato de ela esclarecer o benefício de usar (ou similar), como sugerido acima e na resposta de matt .
grep -P '\d{4}' file | grep -Pv '\d{5}'
Dessa forma, fica claro que seu objetivo é selecionar linhas que contêm uma coisa, mas não outra. Além disso, a sintaxe é mais simples (portanto, pode ser compreendida mais rapidamente por muitos leitores / mantenedores).
fonte
Isso mostrará quatro números seguidos, mas não mais
Observe que ^ significa não
Há um problema com isso, embora não tenha certeza de como corrigir ... se o número for o fim da linha, ele não aparecerá.
Esta versão mais feia, no entanto, funcionaria nesse caso
fonte
a12345b
, porque combina2345b
.Se
grep
não suportar expressões regulares perl (-P
), use o seguinte comando shell:onde
printf '[0-9]%.0s' {1..4}
produzirá 4 vezes[0-9]
. Esse método é útil quando você tem dígitos longos e não deseja repetir o padrão (substitua4
pelo número de dígitos que deseja procurar).Usar
-w
procurará as palavras inteiras. No entanto, se você estiver interessado em cadeias alfanuméricas, como1234a
, adicione[^0-9]
no final do padrão, por exemploUsar
$()
é basicamente uma substituição de comando . Verifique esta postagem para ver como seprintf
repete o padrão.fonte
Você pode tentar o comando abaixo, substituindo pelo
file
nome do arquivo real no seu sistema:Você também pode verificar este tutorial para obter mais usos do comando grep.
fonte