Estou tentando entrar em algo que retornará apenas o primeiro "parágrafo" ou "seção" separado por uma linha em branco. Achei que poderia usar awk
or sed
para obter um intervalo de acordo com outras respostas, mas não parece funcionar.
$ cat txt
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.4-0ubuntu1
Supported: 3y
$ cat txt |awk '/^Package:/,/^$/'
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.4-0ubuntu1
Supported: 3y
Não deveria retornar apenas a primeira "seção"? (conforme: Grep começando de um texto fixo, até a primeira linha em branco e https://www.unix.com/shell-programming-and-scripting/148692-awk-script-match-pattern-till-blank-line.html)
- Se eu usar
grep -ve ^$
as linhas em branco serão removidas, então não há caracteres especiais. Se eu tentar extrair uma parte diferente, obtenho as partes de ambas as "seções":
$ cat txt |awk '/^Package:/,/^Version:/' Package: plasma-desktop Architecture: amd64 Version: 4:5.12.9.1-0ubuntu0.1 Package: plasma-desktop Architecture: amd64 Version: 4:5.12.4-0ubuntu1
Se eu usar
sed -n '/^Package:/,/^$/p'
oused -n '/^Package:/,/^Version:/p'
obtiver os mesmos resultados que o equivalente awk.
Como faço para obter awk
ou sed
parar após a primeira ocorrência?
Responder1
É exatamente por isso que o awk tem um modo de parágrafo:
$ awk -v RS= 'NR==1' file
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y
e imprimir o segundo registro é apenas a mudança óbvia de NR==1
para NR==2
:
$ awk -v RS= 'NR==2' file
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.4-0ubuntu1
Supported: 3y
Nunca use expressões de intervalo - elas tornam o código para problemas triviais um pouco mais breve do que usar um sinalizador, mas se seus requisitos mudarem um pouco, será necessária uma reescrita completa ou condições duplicadas. Então, sempre que você quiser usar /begin/,/end/
com sed ou awk, use /begin/{f=1} f{print} /end/{f=0}
com awk e isso lhe dará MUITO mais controle sobre quando/como imprimir as linhas de início/fim, etc.
Responder2
No /begin/,/end/
, os "sinalizadores de ação" são ativados sempre que uma /begin/
correspondência é encontrada e desativados quando /end/
a correspondência é encontrada. As linhas de limite com “início” e “fim” também são impressas.
As consequências para sua entrada são (as linhas impressas têm um comentário depois delas nos exemplos abaixo):
- Com
'/^Package:/,/^$/'
:
Package: plasma-desktop #TURN ON
Architecture: amd64 #
Version: 4:5.12.9.1-0ubuntu0.1 #
Supported: 3y #
#TURN OFF
Package: plasma-desktop #TURN ON
Architecture: amd64 #
Version: 4:5.12.4-0ubuntu1 #
Supported: 3y #
- Com
'/^Package:/,/^Version:/'
:
Package: plasma-desktop #TURN ON
Architecture: amd64 #
Version: 4:5.12.9.1-0ubuntu0.1 #TURN OFF
Supported: 3y
Package: plasma-desktop #TURN ON
Architecture: amd64 #
Version: 4:5.12.4-0ubuntu1 #TURN OFF
Supported: 3y
Para imprimir apenas o parágrafo começando em "Pacote:" você pode escrever
sed -ne '/^$/q' -e '/^Package:/,$p' file
sed
encerra o processamento do arquivo assim que encontra uma linha em branco por causa de /^$/q
.
Com awk
:
awk '/^$/{exit};/^Package:/,0' file
Responder3
Como comentado porQuasímodo
/begin/,/end/
obtém linhas que correspondem a essas expressões regulares, incluindo as linhas de limite. start liga a impressão e end desliga. A linha logo após a sua linha em branco ativa a impressão novamente, pois também contémPackage:
.
Percebi que posso usar sed
e alterar o /begin/
to 0
e ele começará do início. Como há apenas um começo, ele corresponderá apenas uma vez.
$ cat txt |sed -n '0,/^$/p'
Package: plasma-desktop
Architecture: amd64
Version: 4:5.12.9.1-0ubuntu0.1
Supported: 3y