Mostrar todo o arquivo até a partida

71

grep --before-context 5 mostra 5 linhas antes da partida.

Eu quero mostrar tudo antes da partida.
Fazer grep --before-context 99999999funcionaria, mas não é muito ... profissional.

Como mostrar todo o arquivo até a partida?

Nicolas Raoul
fonte

Respostas:

95

Sed é melhor para isso.

Apenas faça:

sed '/PATTERN/q' FILE

Funciona assim:

Para cada linha, verificamos se corresponde /PATTERN:

  • se sim, imprimimos e fechamos
  • caso contrário, imprimimos

Esta é a solução mais eficiente, porque assim que vê PATTERN, sai. Sem q, o sed continuaria lendo o restante do arquivo e não faria nada com ele. Para arquivos grandes, isso pode fazer a diferença.

Este truque também pode ser usado para emular head:

sed 10q FILE
Mikel
fonte
Apenas tentei, ele apenas gera a primeira linha do arquivo ... mesmo que a correspondência esteja na linha 38.
Nicolas Raoul
Funciona bem para mim. Você pode dar um exemplo de entrada e saída reais? E o comando que você está executando como está.
Mikel
Eu já havia tentado o seu comando antes de editado, foi: sed '/ PADRÃO / p; q' ARQUIVO
Nicolas Raoul
7
O que devo fazer se não quiser imprimir a linha com a correspondência de padrões?
tommy.carstensen
4
@ tommy.carstensen: sed '/PATTERN/Q' FILEpula a linha correspondente. Qé uma extensão GNU, portanto não funcionará com nenhum sed.
Alex O
37

sed pode substituir a maioria das funcionalidades do grep.

sed -n '1,/<pattern>/ p' <file>

Isso significa imprimir da primeira linha até o padrão ser correspondido.

Alguns exemplos de intervalo

sed -n '/<pattern>/,$ p' <file> # from pattern to end of file
sed -n '/<pattern1>/,/<pattern2>/ p' <file> # from pattern1 to pattern2
forcefsck
fonte
3
Este comando é bom, mas você pode fazer melhor. Dessa forma, ele lê o arquivo inteiro, mas é possível sair assim que encontrar uma correspondência.
Mikel
3
O que devo fazer se não quiser imprimir a linha com a correspondência de padrões?
tommy.carstensen
34

imprima até e inclua a correspondência:

awk '{print} /pattern/ {exit}' filename
sed '/pattern/q' filename

imprima até, mas NÃO incluindo a correspondência:

awk '/pattern/ {exit} {print}' filename
sed '/pattern/Q' filename
Glenn Jackman
fonte
11
Qo afaik legal, mas específico do GNU, sed -n '/pattern/!p;//q'seria mais portátil.
don_crissti
@don_crissti: você deve fazer uma resposta, eu acho que é uma boa resposta (porém: eu sou um pouco curiosa como funciona. Eu acredito !que paplica-se a linhas que não correspondem pattern, mas isso //qme confunde ...
jwd
2
@don_crissti: ah, entendi - //significa "expressão regular anterior" (eu pensei que isso significava "corresponder à string vazia"). Eu acho que uma versão mais curta da mesma solução é sed -n '/pattern/q;p:?
jwd 22/08
@ jwd - na verdade, é mais curto. Don
don_crissti
1

Os seguintes grepmétodos GNU puros não são eficientes.

Pesquise tudo, até a primeira instância da string " foo " na barra de arquivos , usando três greps:

grep -m 1 -B $(grep -n -m 1 foo bar | grep -o '^[0-9]*') foo bar

Correspondendo até a última instância de " foo ":

grep -oPz "(?s)[^\n]*${s}.*?\n.*?foo.*?\n" bar

Nota: detalhes sobre o último greppodem ser encontrados em: Regex (grep) para pesquisa em várias linhas necessária .

agc
fonte
Por que alguém iria querer usar 7 greps (+ pcre) quando se trata de simplesmente executar uma única sedinvocação: sed 'x;/./G;//!x;/foo/p;//s/.*//;x;d'??
31416 Dom
@don_crissti, seu sedcódigo parece ter sua própria resposta ou pode ser adicionado a um dos outros. Re 7 greps: Como não houve grepresposta ... (além disso, a resposta ajuda a mostrar por que não.) #
Agc
Não é o meu código, basta clicar nele ... Mesmo se esse fosse o meu código, ele não responde o Q aqui, então eu não o postaria como resposta.
31126 don_crissti
1

Adicionando à resposta de Mikel acima ...


Para imprimir todas as linhas até, mas não incluindo , a primeira linha que FILEcontém PATTERN, tente:

  • sed '/.*PATTERN.*/{s///;q;}' FILE

Isso corresponde à linha inteira que contém o padrão, substitui-o por uma linha em branco e sai sem processar o restante do arquivo.


Pós-script:

A maneira mais fácil / clara de evitar a impressão de uma nova linha extra no final (sem envolver outra ferramenta), era executar o sed novamente e excluir a nova linha final:

sed '/.*PATTERN.*/{s///;q;}' FILE | sed '$d'

... e como agora estamos excluindo essa linha de qualquer maneira, nosso trabalho anterior é redundante e podemos simplificar:

sed '/PATTERN/q' FILE | sed '$d'
Jim Grisham
fonte
A resposta de Glenn - e meu comentário aqui - mostra como fazer isso com uma única sedinvocação.
311217 don_crissti
(Obrigado por isso -. Eu vi o seu comentário sobre a resposta da AGC mas ou perdeu o outro ou apenas desnatado porque meu cérebro não gosta duplas-negativos) Desde que eu estava usando isso em tanto um tcshe um bashapelido, eu precisava me certificar de que tinha uma solução de linha única relativamente concisa que funcionava tanto no padrão quanto no GNU sed(para portabilidade); todos os requisitos que sua contribuição pode muito bem ter cumprido. Como alguém que usa sed muito raramente, meu requisito mais importante era algo que eu pudesse entender rapidamente quando quiser editar ou redirecioná-lo facilmente daqui a alguns anos.
Jim Grisham
1

Para pessoas que optam por se lembrar apenas das ferramentas básicas do dia-a-dia e dispostas a aceitar soluções menos elegantes e menos eficientes:

head -n $(grep -n pattern filename | cut -d: -f1) filename

Se esse comando for para um script, procurarei soluções mais elegantes (e possivelmente eficientes). Se esse é um comando único ou um script descartável, não me importo.

lesmana
fonte
11
Boa idéia, mas três comandos quando um fará.
23411 Mikel
11
Conhecer o básico é realmente muito bom. Conhecer a ferramenta certa para o trabalho é melhor, no entanto.
soulmerge
Se esse comando for para um script, procurarei soluções mais elegantes (e possivelmente eficientes). Se este é um comando único (ou um script descartável), não me importo.
Lesmana
0

Você também pode usar um dos seguintes

tac ./test | grep -B $(cat ./test | wc -l) -m 1 'pattern'|tac 

ou

tac ./test |head -n $(tac ./test | grep -n 'pattern' | cut -d: -f1 | head -n 1)|tac

ou

tac ./test |sed ':a;N;$!ba;s/\n/'"pattern"'/g' | sed 's/'"patternpattern"'/\n/g'|head -n 1|sed 's/'"pattern"'/\n/g'|tac

A primeira opção é muito semelhante à sugerida pelo OP, mas garante que você mostre linhas suficientes antes do contexto, contando as linhas no arquivo

A segunda opção pesquisa o número da linha da primeira correspondência (você também pode alterar isso alterando a 'cabeça' interna) e depois usa a cabeça nesse número

A última opção substitui todas as novas linhas pela correspondência e, em seguida, substitui duas correspondências adjacentes por uma nova linha. A saída disso é uma linha para cada bloco de texto entre duas correspondências. Depois disso, ele usa 'head' para escolher a primeira linha (mantendo o bloco de texto até a primeira correspondência) correspondente e depois traduz novamente cada correspondência para uma nova linha. essa opção funciona apenas se o arquivo estiver no seguinte formato

pattern
texttexttext
texttexttext texttexttext
texttexttexttexttexttexttexttexttext
pattern
texttexttext
pattern 
texttexttext
texttexttexttexttexttexttexttexttext

e assim por diante

user122778
fonte
2
considere explicar como isso funciona, principalmente porque esse sedcomando na parte inferior é meio complicado.
21815 strugee
a primeira opção é muito semelhante ao que o OP sugerida apenas certifica-se de ti mostrar kines suficiente antes de contexto através da contagem de linhas na filr,
user122778