
Eu tenho uma variável chamada descr
que pode conter uma string Blah: -> r1-ae0-2 / [123]
, -> s7-Gi0-0-1:1-US / Foo
etc. Quero obter a -> r1-ae0-2
parte -> s7-Gi0-0-1:1-US
da string. No momento eu uso descr=$(grep -oP '\->\s*\S+' <<< "$descr"
para isso. Existe uma maneira melhor de fazer isso? Também é possível fazer isso com expansão de parâmetros?
Responder1
ksh93
e zsh
ter suporte de referência anterior (ou mais precisamente 1 , referências para grupos de captura na substituição) dentro de ${var/pattern/replacement}
, não bash
.
ksh93
:
$ var='Blah: -> r1-ae0-2 / [123]'
$ printf '%s\n' "${var/*@(->*([[:space:]])+([^[:space:]]))*/\1}"
-> r1-ae0-2
zsh
:
$ var='Blah: -> r1-ae0-2 / [123]'
$ set -o extendedglob
$ printf '%s\n' "${var/(#b)*(->[[:space:]]#[^[:space:]]##)*/$match[1]}"
-> r1-ae0-2
( mksh
a página man também menciona que versões futuras irão suportá-lo ${KSH_MATCH[1]}
para o primeiro grupo de captura. Ainda não disponível em 25/04/2017).
No entanto, com bash
você pode fazer:
$ [[ $var =~ -\>[[:space:]]*[^[:space:]]+ ]] &&
printf '%s\n' "${BASH_REMATCH[0]}"
-> r1-ae0-2
O que é melhor, pois verifica se o padrão foi encontrado primeiro.
Se as expressões regulares do seu sistema suportarem \s
/ \S
, você também poderá fazer:
re='->\s*\S+'
[[ $var =~ $re ]]
Com o zsh
, você pode obter todo o poder dos PCREs com:
$ set -o rematchpcre
$ [[ $var =~ '->\s*\S+' ]] && printf '%s\n' $MATCH
-> r1-ae0-2
Com zsh -o extendedglob
, veja também:
$ printf '%s\n' ${(SM)var##-\>[[:space:]]#[^[:space:]]##}
-> r1-ae0-2
Portavelmente:
$ expr " $var" : '.*\(->[[:space:]]*[^[:space:]]\{1,\}\)'
-> r1-ae0-2
Se houver diversas ocorrências do padrão na string, o comportamento irá variar com todas essas soluções. No entanto, nenhum deles fornecerá uma lista separada por nova linha de todas as correspondências, como na sua grep
solução baseada em GNU.
Para fazer isso, você precisa fazer o loop manualmente. Por exemplo, com bash
:
re='(->\s*\S+)(.*)'
while [[ $var =~ $re ]]; do
printf '%s\n' "${BASH_REMATCH[1]}"
var=${BASH_REMATCH[2]}
done
Com zsh
, você poderia recorrer a esse tipo de truque para armazenar todas as correspondências em um array:
set -o extendedglob
matches=() n=0
: ${var//(#m)->[[:space:]]#[^[:space:]]##/${matches[++n]::=$MATCH}}
printf '%s\n' $matches
1 referências anteriores designam mais comumente um padrão que faz referência ao que foi correspondido por um grupo anterior. Por exemplo, a \(.\)\1
expressão regular básica corresponde a um único caractere seguido pelo mesmo caractere (corresponde a on aa
, não a on ab
). Essa \1
é uma referência anterior a esse \(.\)
grupo de captura no mesmo padrão.
ksh93
suporta referências anteriores em seus padrões (por exemplo, ls -d -- @(?)\1
listará os nomes de arquivos que consistem em dois caracteres idênticos), não outros shells. BREs e PCREs padrão suportam referências anteriores, mas não ERE padrão, embora algumas implementações de ERE o suportem como uma extensão. bash
's [[ foo =~ re ]]
usa EREs.
[[ aa =~ (.)\1 ]]
não vai combinar, mas
re='(.)\1'; [[ aa =~ $re ]]
pode se os EREs do sistema o suportarem.
Responder2
Você deseja excluir tudo até o primeiro ␣->␣
(sem incluir a "seta") e depois do último ␣/
(incluindo o espaço e a barra).
string="Blah: -> r1-ae0-2 / [123]"
string=${string/*->/->}
string=${string/ \/*}
$string
será agora -> r1-ae0-2
.
As mesmas duas substituições se transformariam -> s7-Gi0-0-1:1-US / Foo
em -> s7-Gi0-0-1:1-US
.
Responder3
Responder definitivamente é impossível sem saber o formato exatotodomensagem leva. No entanto, como abordagem geral, você pode imprimir determinados campos específicos usando cut
:
$ cut -d ' ' -f 2 <<< '-> s7-Gi0-0-1:1-US / Foo'
s7-Gi0-0-1:1-US
Ou você podeimprima cada enésima coluna usandoawk
:
$ awk -F' ' '{ for (i=2;i<=NF;i+=4) print $i }' <<< '-> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Foo'
r1-ae0-2
s7-Gi0-0-1:1-US