Como imprimir todas as linhas após uma correspondência até o final do arquivo?


48

O arquivo de entrada1 é:

dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Atribuo à correspondência o padrão de other file(como dog 123 4335no arquivo2).

Eu igualo o padrão da linha dog 123 4335e, depois de imprimir todas as linhas sem a linha correspondente, minha saída é:

cat 13123 23424
deer 2131 213132
bear 2313 21313

Se usar apenas sem endereço da linha, use apenas o padrão, por exemplo, 1s como combinar e imprimir as linhas?


Outro arquivo pode conter apenas um único padrão para pesquisar ou um por linha e iniciar a pesquisa na linha que for encontrada primeiro no arquivo pesquisado?
Ciro Santilli

Respostas:


27

Supondo que você queira combinar toda a linha com seu padrão, com o GNU sed, isso funciona:

sed -n '/^dog 123 4335$/ { :a; n; p; ba; }' infile

Equivalente padrão:

sed -ne '/^dog 123 4335$/{:a' -e 'n;p;ba' -e '}' infile

Com a seguinte entrada ( infile):

cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

A saída é:

cat 13123 23424 
deer 2131 213132
bear 2313 21313

Explicação:

  • /^dog 123 4335$/ procura o padrão desejado.
  • :a; n; p; ba;é um loop que busca uma nova linha de input ( n), imprime ( p) e ramifica de volta para rotular a :a; ...; ba;.

Atualizar

Aqui está uma resposta que se aproxima mais de suas necessidades, ou seja, padrão no arquivo2, grepping no arquivo1:

tail -n +$(( 1 + $(grep -m1 -n -f file2 file1 | cut -d: -f1) )) file1

O grep and cut incorporado encontra a primeira linha que contém um padrão do arquivo2, esse número de linha mais um é passado para o final, o mais existe para pular a linha com o padrão.

Se você deseja começar da última partida em vez da primeira, seria:

tail -n +$(( 1 + $(grep -n -f file2 file1 | tail -n1 | cut -d: -f1) )) file1

Observe que nem todas as versões da cauda suportam a notação positiva.


Este é o primeiro exemplo dos comandos n e p no sed que eu já vi que não parece levar muito o sed. Parece (pelos meus breves testes) que sed -n '/^dog 123 4335$/ { :a; p; n; ba; }' infile(com os botões n e alternados) inclui com êxito a linha que corresponde também.
Josiah Yoder

26

Se você tiver apenas um arquivo razoavelmente curto grep, pode funcionar:

grep -A5000 -m1 -e 'dog 123 4335' animals.txt

5000 é apenas o meu palpite de "razoavelmente curto", pois grepencontra a primeira correspondência e a produz junto com as próximas 5000 linhas (o arquivo não precisa ter tantas). Se você não quiser a correspondência, precisará cortá-la, por exemplo,

grep -A5000 -m1 -e 'dog 123 4335' animals.txt | tail -n+2


Se você não quiser a primeira, mas a última correspondência como delimitador, poderá usar isso:

tac animals.txt | sed -e '/dog 123 4335/q' | tac

Essa linha lê animals.txtna ordem inversa das linhas e gera até e inclusive a linha com dog 123 4335e, em seguida, reverte novamente para restaurar a ordem correta.

Novamente, se você não precisar da correspondência no resultado, acrescente a cauda. (Você também pode complicar a expressão sed para descartar seu buffer antes de sair.)


No meu teste, o GNU grep 3.0 não gera mais de 132 linhas no pós-contexto (independentemente do valor especificado).
Ruvim 29/08

22

Na prática, eu provavelmente usaria a resposta de Aet3miirah na maioria das vezes e a resposta de alexey é maravilhosa quando se deseja navegar pelas linhas (também, também funciona less). OTOH, eu realmente gosto de outra abordagem (que é o tipo de resposta de Gilles invertida :

sed -n '/dog 123 4335/,$p'

Quando chamado com o -nsinalizador, sednão imprime por padrão as linhas que processa mais. Em seguida, usamos um formulário de dois endereços que diz para aplicar um comando da linha correspondente /dog 123 4335/até o final do arquivo (representado por $). O comando em questão é p, que imprime a linha atual. Portanto, isso significa "imprimir todas as linhas da que corresponde /dog 123 4335/ao final".


3
Isso imprime a doglinha que não é desejada aqui.
Stéphane Chazelas

11
Essa parece a melhor resposta (e funciona para o meu próprio caso), mas precisaria ser adaptada para pular a linha correspondente também.
Pavel Šimerda 14/03

11
sed -n '/ cão 123 4335 /, $ p' | sed '1d' irá remover a linha de cão
Kemin Zhou

11
sed -n '/dog 123 4335/,$p' | tail -n +2irá remover a partida também
gilad mayani 24/10

15
sed -e '1,/dog 123 4335/d' file1

Se você precisar ler o padrão de um arquivo, substitua-o no comando sed. Se o arquivo contiver um padrão sed:

sed -e "1,/$(cat file2)/d" file1

Se o arquivo contiver uma cadeia literal para procurar, cite todos os caracteres especiais. Presumo que o arquivo contenha uma única linha.

sed -e "1,/$(sed 's/[][\\\/^$.*]/\\&/g' file2)/d" file1

Se você deseja que a correspondência seja a linha inteira, não apenas uma substring, envolva o padrão ^…$.

sed -e "1,/^$(sed 's/[][\\\/^$.*]/\\&/g' file2)\$/d" file1

6
Isso não funcionará se o padrão estiver na primeira linha. O GNU sedtem 0,/dog.../dpara isso.
Stéphane Chazelas

14

$ more +/"dog 123 4335" file1


4
Também trabalha com less.
Brandizzi

3
inteligente no terminal, mas na verdade não funciona se você conectá-lo a outra coisa tac.
21415 javeau_ictx

Estou usando-o assim, $ more + / "match my words" file1 >> file2
AMB

11
Talvez tenha +sido substituído pelo -pPOSIX 7: pubs.opengroup.org/onlinepubs/9699919799/utilities/more.html, mas ainda não implementado no util-linux 2.20.1. E isso também imprime skipping..e algumas novas linhas extras (para stderr, espero, pode ser bom).
Ciro Santilli publicou 14/09/16

Talvez as coisas mudaram desde então? o meu comentário tem 3 upvotes por isso pode ter sido relevante no momento ...
jcomeau_ictx

11

Com awk:

awk 'BEGIN {getline pattern < "other file"}
   NR == 1, $0 ~ pattern {next}; {print}' < "input file"

5

Uma maneira de usar o awk:

awk 'NR==FNR{a[$0];next}f;($0 in a){f=1}'  file2 file1

onde file2 contém seus padrões de pesquisa. Primeiro, todo o conteúdo do arquivo2 é armazenado na matriz "a". Quando o arquivo1 é processado, todas as linhas são verificadas na matriz e impressas apenas se não estiverem presentes.


Eu acho que o OP quer produzir todas as linhas seguindo o padrão.
Thor

@ Thor: obrigado por apontar, atualizou agora ...
Guru

Bem feito :).
Thor

5

Se a entrada for um arquivo regular obrigatório :

Com o GNU grep:

{ grep  -xFm1 'dog 123 4335' >&2
  cat; } <infile 2>/dev/null >outfile

Com sed:

{ sed -n '/^dog 123 4335$/q'
  cat; } <infile >outfile

Um GNU grepchamado w / the -moption encerrará a entrada na partida - e deixará sua entrada (lseekable) fd imediatamente após o ponto em que encontrou sua última correspondência. Assim, chamar grepw / -m1encontra a primeira ocorrência de um padrão em um arquivo e deixa o deslocamento de entrada exatamente no lugar certo para catescrever tudo após a primeira correspondência do padrão em um arquivo para stdout.

Mesmo sem um GNU, grepvocê pode fazer exatamente a mesma coisa com um compatível com POSIX sed- quandosed q usuário é especificado, deixa seu deslocamento de entrada exatamente onde está. O GNU sednão é compatível com os padrões dessa maneira, portanto, o que foi dito acima provavelmente não funcionará com um GNU, a sedmenos que você o chame com seu -uswitch.


note que o sedcompartilhamento de fluxo demonstrado aqui não é especialmente (embora sim, o padrão referenciado faça um exemplo específico sedcomo um utilitário assim capaz) do fluxo de trabalho de forma livre e condicionalmente cooperativo mostrado. notavelmente, todos os utilitários padrão foram criados e especificados para cooperar e compartilhar as posições dos cursores dos fluxos de entrada sem falhar no próximo leitor qualquer processamento. grep -qdeve fazer isso; silenciosamente grepdeve retornar assim que qualquer correspondência na entrada for encontrada, e qualquer restante da entrada não deve, por padrão, ser consumido por padrão.
mikeserv

4

Minha resposta para a pergunta no assunto, sem armazenar padrão em um segundo arquivo. Aqui está o meu arquivo de teste:

$ cat animals.txt 
cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

GNU sed:

 $ sed '0,/^dog 123 4335$/d' animals.txt 
 cat 13123 23424 
 deer 2131 213132
 bear 2313 21313

Perl:

$ perl -ne 'print unless 1.../^dog 123 4335$/' animals.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Variante Perl com padrão em um arquivo:

$ cat pattern.txt 
dog 123 4335
$ perl -ne 'BEGIN{chomp($p=(<STDIN>)[0])};print unless 1../$p/;' animals.txt < pattern.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

2

Wth ed:

ed -s file1 <<< '/dog 123 4335/+1,$p'

Isso envia um pcomando rint para ed em uma string here; o comando de impressão é limitado no intervalo a um após ( +1) a dog 123 4335correspondência até o final do arquivo ( $).


1

Se você não se importa com a criação de um arquivo temporário, e tem csplitdisponível, isso funciona:

sh -c 'csplit -sf"$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

Nota file1é o arquivo de entrada efile2 é o arquivo de padrão (conforme indicado na pergunta).

A forma longa do comando acima é:

sh -c 'csplit --quiet --prefix="$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

ou seja,

csplit --quiet --prefix="file1_" "file1" "%^$(cat "file2")%+1" && cat "file1_00"

csplitsem o prefixsinalizador acima criaria o arquivo xx00(sendo o prefixo xxe o sufixo 00). Com a bandeira acima, ele cria o arquivo file1_00. Sem o quietsinalizador, ele imprime o tamanho do arquivo de saída (tamanho do arquivo resultante).


0

Como o awk não é expressamente proibido, eis a minha oferta assumindo que 'gato' é a combinação.

awk '$0 ~ /cat/ { vart = NR }{ arr[NR]=$0 } END { for (i = vart; i<=NR ; i++) print arr[i]  }' animals.txt

0

Como imprimir todas as linhas após uma correspondência até o final do arquivo?

Outra maneira de colocar isso é "como excluir todas as linhas da 1ª até a partida (incluindo)", e isso pode ser sedescrito como:

sed -e '1,/MATCH PATTERN/d'

11
O único problema é quando o padrão é na primeira linha ...
don_crissti


Acho que precisamos de um comitê aqui para decidir.
poige 14/09/16

11
@poige: nah, você fornece a mesma resposta menos abrangente
Thor

@don_crissti, que tal sed -e '0,/MATCH PATTERN/d'então?
Velkan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.