Quero encontrar arquivos que tenham "abc" AND "efg" nessa ordem e essas duas seqüências de caracteres estejam em linhas diferentes nesse arquivo. Por exemplo: um arquivo com conteúdo:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
Deve ser correspondido.
Respostas:
Grep não é suficiente para esta operação.
O pcregrep, encontrado na maioria dos sistemas Linux modernos, pode ser usado como
onde
-M
,--multiline
permita que os padrões correspondam a mais de uma linhaHá um pcre2grep mais recente também. Ambos são fornecidos pelo projeto PCRE .
pcre2grep está disponível para Mac OS X através de portas Mac como parte da porta
pcre2
:e via Homebrew como:
ou para pcre2
pcre2grep também está disponível no Linux (Ubuntu 18.04+)
fonte
-M, --multiline
- permite que os padrões correspondam a mais de uma linha.'abc.*(\n|.)*?efg'
.*
->'abc(\n|.)*?efg'
para fazer a regex mais curto (e para ser pedante)pcregrep
facilita as coisas, masgrep
também funciona. Por exemplo, consulte stackoverflow.com/a/7167115/123695Não tenho certeza se é possível com o grep, mas o sed facilita muito:
fonte
sed
, mas se nunca vi essa expressão antes.Aqui está uma solução inspirada nesta resposta :
se 'abc' e 'efg' puderem estar na mesma linha:
se 'abc' e 'efg' devem estar em linhas diferentes:
Params:
-z
Trate a entrada como um conjunto de linhas, cada uma terminada por um byte zero em vez de uma nova linha. ie grep trata a entrada como uma linha grande.-l
imprima o nome de cada arquivo de entrada do qual a saída normalmente seria impressa.(?s)
ativar PCRE_DOTALL, o que significa que '.' localiza qualquer caractere ou nova linha.fonte
l
. AFAIK não há-1
opção de número .-z
opções especificam grep para tratar as novas linhas,zero byte characters
então porque precisamos do(?s)
no regex? Se já é um caractere que não.
é de nova linha, não deve ser possível correspondê-lo diretamente?sed deve ser suficiente como o pôster LJ indicado acima,
em vez de! d, você pode simplesmente usar p para imprimir:
fonte
Eu confiei muito no pcregrep, mas com o grep mais recente, você não precisa instalar o pcregrep para muitos de seus recursos. Apenas use
grep -P
.No exemplo da pergunta do OP, acho que as seguintes opções funcionam bem, com a segunda melhor correspondência de como eu entendo a pergunta:
Copiei o texto como / tmp / test1 e excluí o 'g' e salvei como / tmp / test2. Aqui está a saída que mostra que o primeiro mostra a sequência correspondente e o segundo mostra apenas o nome do arquivo (típico -o é para mostrar correspondência e típico -l é para mostrar apenas nome do arquivo). Observe que o 'z' é necessário para a multilinha e o '(. | \ N)' significa corresponder 'qualquer coisa que não seja nova linha' ou 'nova linha' - ou seja, qualquer coisa:
Para determinar se sua versão é nova o suficiente, execute
man grep
e veja se algo semelhante a esse aparece na parte superior:Isso é do GNU grep 2.10.
fonte
Isso pode ser feito facilmente usando primeiro
tr
para substituir as novas linhas por algum outro caractere:Aqui, estou usando o caractere de alarme
\a
(ASCII 7) no lugar de uma nova linha. Isso quase nunca é encontrado no seu texto, egrep
pode corresponder a um.
, ou especificamente a ele\a
.fonte
\0
e, portanto, necessáriogrep -a
e combinando\x00
... Você me ajudou a simplificar!echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
agora éecho $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
.awk one-liner:
fonte
abc
final ao final do arquivo se o padrão final não estiver presente no arquivo ou se o último padrão final estiver ausente. Você pode corrigir isso, mas isso complicará bastante o script./efg/
da saída?Você pode fazer isso com muita facilidade se puder usar o Perl.
Você pode fazer isso com uma única expressão regular também, mas isso envolve levar todo o conteúdo do arquivo em uma única sequência, o que pode acabar consumindo muita memória com arquivos grandes. Para completar, eis o método:
fonte
.*?
) para obter uma correspondência mínima.Não sei como faria isso com o grep, mas faria algo assim com o awk:
Você precisa ter cuidado ao fazer isso, no entanto. Deseja que o regex corresponda à substring ou à palavra inteira? adicione tags \ w, conforme apropriado. Além disso, embora isso esteja em conformidade estrita com a maneira como você citou o exemplo, ele não funciona quando abc aparece uma segunda vez após o efg. Se você quiser lidar com isso, adicione um if conforme apropriado no / abc / case etc.
fonte
Infelizmente, você não pode. Dos
grep
documentos:fonte
grep -Pz
Se você estiver disposto a usar contextos, isso pode ser alcançado digitando
Isso exibirá tudo entre "abc" e "efg", desde que estejam a 500 linhas um do outro.
fonte
Se você precisar que as duas palavras estejam próximas umas das outras, por exemplo, não mais que 3 linhas, você pode fazer o seguinte:
Mesmo exemplo, mas filtrando apenas arquivos * .txt:
E também você pode substituir
grep
comando poregrep
comando, se desejar encontrar também com expressões regulares.fonte
Lancei uma alternativa grep há alguns dias atrás, que suporta isso diretamente, seja por correspondência multilinha ou usando condições - espero que seja útil para algumas pessoas que pesquisam aqui. É assim que os comandos do exemplo se pareceriam:
Multilinha:
Condições:
Você também pode especificar que 'efg' deve seguir 'abc' dentro de um certo número de linhas:
Você pode encontrar mais informações em sift-tool.org .
fonte
sift -lm 'abc.*efg' testfile
funcione, porque a correspondência é gananciosa e devora todas as linhas até a últimaefg
no arquivo.Embora a opção sed seja a mais simples e fácil, o one-liner do LJ infelizmente não é o mais portátil. Aqueles presos com uma versão do C Shell precisarão escapar da franja:
Infelizmente, isso não funciona em bash et al.
fonte
fonte
você pode usar o grep, caso não esteja interessado na sequência do padrão.
exemplo
grep -l
encontrará todos os arquivos que correspondem ao primeiro padrão, e xargs fará grep para o segundo padrão. Espero que isto ajude.fonte
Com o pesquisador prateado :
semelhante à resposta do portador do anel, mas com um ag. As vantagens de velocidade do buscador de prata podem brilhar aqui.
fonte
(echo abctest; echo efg)|ag 'abc.*(\n|.)*efg'
não correspondeEu usei isso para extrair uma sequência fasta de um arquivo multi fasta usando a opção -P para grep:
O núcleo do regexp é o
[^>]
que se traduz em "não maior que o símbolo"fonte
Como uma alternativa para a resposta de Balu Mohan, é possível impor a ordem dos padrões usando apenas
grep
,head
etail
:Este não é muito bonito, no entanto. Formatado mais facilmente:
Isto irá imprimir os nomes de todos os arquivos onde
"pattern2"
aparece depois"pattern1"
, ou onde ambos aparecem na mesma linha :Explicação
tail -n +i
- imprimir todas as linhas após oi
th, inclusivegrep -n
- acrescente as linhas correspondentes aos seus números de linhahead -n1
- imprime apenas a primeira linhacut -d : -f 1
- imprima a primeira coluna cortada usando:
como delimitador2>/dev/null
-tail
saída de erro de silêncio que ocorre se a$()
expressão retornar vaziagrep -q
- silenciegrep
e retorne imediatamente se uma correspondência for encontrada, pois estamos interessados apenas no código de saídafonte
&>
? Também estou usando, mas nunca o vi documentado em lugar algum. BTW, por que temos que silenciar o grep dessa maneira, na verdade?grep -q
não vai fazer o truque também?&>
diz ao bash para redirecionar a saída padrão e o erro padrão, consulte REDIRECÇÃO no manual do bash. Você está muito certo no que poderíamos fazer emgrep -q ...
vez degrep ... &>/dev/null
, boa captura!Isso deve funcionar também ?!
$ARGV
contém o nome do arquivo atual ao ler asfile_list /s
pesquisas de modificadores na nova linha.fonte
O padrão de arquivo
*.sh
é importante para impedir que os diretórios sejam inspecionados. É claro que alguns testes podem impedir isso também.o
pesquisa no máximo 1 correspondência e retorna (-n) o número da roupa. Se uma correspondência foi encontrada (teste -n ...), encontre a última correspondência do efg (encontre tudo e leve a última com a cauda -n 1).
mais continue.
Como o resultado é algo como
18:foofile.sh String alf="abc";
precisamos cortar ":" até o final da linha.Deve retornar um resultado positivo se a última correspondência da 2ª expressão tiver passado da primeira correspondência da primeira.
Em seguida, reportamos o nome do arquivo
echo $f
.fonte
Por que não algo simples como:
retorna 0 ou um número inteiro positivo.
egrep -o (mostra apenas correspondências, truque: várias correspondências na mesma linha produzem saída em várias linhas como se estivessem em linhas diferentes)
grep -A1 abc
(imprima abc e a linha depois)grep efg | wc -l
(Contagem de 0 n de linhas efg encontradas após abc na mesma ou nas linhas seguintes, o resultado pode ser usado em um 'se ")grep pode ser alterado para egrep etc., se for necessária a correspondência de padrões
fonte
Se você tem alguma estimativa sobre a distância entre as duas cadeias 'abc' e 'efg' que está procurando, você pode usar:
Dessa forma, o primeiro grep retornará a linha com o 'abc' mais # num1 linhas depois dele, e # num2 linhas depois dele, e o segundo grep examinará todos eles para obter o 'efg'. Então você saberá em quais arquivos eles aparecem juntos.
fonte
Com o ugrep lançado há alguns meses:
Essa ferramenta é altamente otimizada para velocidade. Também é compatível com GNU / BSD / PCRE-grep.
Observe que devemos usar uma repetição lenta
+?
, a menos que você queira combinar todas as linhasefg
juntas até a últimaefg
no arquivo.fonte
Isso deve funcionar:
Se houver mais de uma correspondência, você poderá filtrar usando grep -v
fonte