sed: imprime apenas o grupo correspondente

133

Quero pegar os dois últimos números (um int, um float; seguido pelo espaço em branco opcional) e imprimi-los apenas.

Exemplo:

foo bar <foo> bla 1 2 3.4

Deve imprimir:

2 3.4

Até agora, tenho o seguinte:

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/replacement/p' 

vai me dar

foo bar <foo> bla 1 replacement

No entanto, se eu tentar substituí-lo pelo grupo 1, toda a linha será impressa.

sed -n  's/\([0-9][0-9]*[\ \t][0-9.]*[\ \t]*$\)/\1/p' 

Como posso imprimir apenas a seção da linha que corresponde à regex do grupo?

mort
fonte

Respostas:

138

Combine a linha inteira, então adicione a .*no início de sua regex. Isso faz com que toda a linha seja substituída pelo conteúdo do grupo

echo "foo bar <foo> bla 1 2 3.4" |
 sed -n  's/.*\([0-9][0-9]*[\ \t][0-9.]*[ \t]*$\)/\1/p'
2 3.4
iruvar
fonte
38
Eu tive que adicionar a -ropção `--regexp-extended`, caso contrário, estava recebendo invalid reference \1 on o erro RHS` do comando s '.
Daniel Sokolowski
15
@DanielSokolowski Eu acho que você recebe esse erro se você usar (e em )vez de \(e \).
Daniel Darabos
3
Lembre-se também de adicionar .*ao final da regexp se a sequência que você deseja extrair nem sempre estiver no final da linha.
Teemu Leisti
3
Isso não vai funcionar para mim porque .*é ganancioso e sed não tem um não-gananciosos.*?
sondra.kinsey
@DanielDarabos Apenas mencione isso (e )não gerará erros no ubuntu 16.04. Então, acho que esse comentário está desatualizado.
Li haonan 6/09/19
72

O grep é a ferramenta certa para extrair.

usando seu exemplo e sua regex:

kent$  echo 'foo bar <foo> bla 1 2 3.4'|grep -o '[0-9][0-9]*[\ \t][0-9.]*[\ \t]*$'
2 3.4
Kent
fonte
12
grande para todo o grupo, embora sed é necessária para grupos individuais
jozxyqk
O grep -o não é portado em sistemas executando o msysgit, mas o sed.
cchamberlain
Veja a pergunta vinculada por @jozxyqk para obter uma resposta que use o look-ahead e o look-behind para resolver isso com grep.
Joachim Breitner
Você pode extrair um grupo de um padrão com grep -ochamadas canalizadas . stackoverflow.com/a/58314379/117471
Bruno Bronosky 10/10
12

E para mais uma opção, eu usaria o awk!

echo "foo bar <foo> bla 1 2 3.4" | awk '{ print $(NF-1), $NF; }'

Isso dividirá a entrada (eu estou usando STDIN aqui, mas sua entrada pode ser facilmente um arquivo) em espaços e, em seguida, imprima o campo menos um, e depois o último campo. As $NFvariáveis ​​mantêm o número de campos encontrados após a explosão nos espaços.

O benefício disso é que não importa se o que precede os dois últimos campos muda, desde que você queira apenas os dois últimos, ele continuará funcionando.

chooban
fonte
3

O comando de corte foi projetado para esta situação exata. Ele será "cortado" em qualquer delimitador e, em seguida, você poderá especificar quais blocos devem ser gerados.

Por exemplo: echo "foo bar <foo> bla 1 2 3.4" | cut -d " " -f 6-7

Resultará na saída de: 2 3.4

-d define o delimitador

-f seleciona o intervalo de 'campos' para saída, neste caso, são os sexto a sétimo a sétimo da seqüência original. Você também pode especificar o intervalo como uma lista, como 6,7.

carlin.scott
fonte
Para imprimir apenas determinadas colunas, vá paraawk '{ print $2" "$6 }'
nurettin 8/18
@ Nurettin Acho que seu comentário pode ter sido para uma das respostas awk.
Carlin.scott
Tentei cortar quando visitei esta página e percebi suas limitações e decidi escrever uma versão mais generalizada no awk como um comentário para melhorar a qualidade deste post.
precisa
1
Sim, acho que isso pertence a uma resposta diferente envolvendo o awk. O comando de corte para fazer o que você escreveu é:cut -d " " -f 2,6
carlin.scott
ah, eu não sabia disso, pensei que você poderia apenas dar intervalos. Obrigado por isso.
precisa
2

Concordo com @kent que isso é adequado grep -o. Se você precisar extrair um grupo dentro de um padrão, poderá fazê-lo com um segundo grep.

# To extract \1 from /xx([0-9]+)yy/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'xx[0-9]+yy' | grep -Eo '[0-9]+'
123
4

# To extract \1 from /a([0-9]+)b/
$ echo "aa678bb xx123yy xx4yy aa42 aa9bb" | grep -Eo 'a[0-9]+b' | grep -Eo '[0-9]+'
678
9
Bruno Bronosky
fonte