O bash suporta referências anteriores na expansão de parâmetros?

15

Eu tenho uma variável chamada descrque pode conter uma string Blah: -> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Fooetc. Eu quero começar a -> r1-ae0-2, -> s7-Gi0-0-1:1-USparte da cadeia de caracteres. No momento eu usodescr=$(grep -oP '\->\s*\S+' <<< "$descr" para isso. Existe uma maneira melhor de fazer isso? Também é possível fazer isso com a expansão de parâmetros?

Martin
fonte

Respostas:

20

ksh93e zshter referência anterior (ou mais precisamente 1 , referências para capturar grupos na substituição), suporte interno ${var/pattern/replacement}, nãobash .

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

(a mkshpágina de manual também menciona que versões futuras o apoiarão com${KSH_MATCH[1]} o primeiro grupo de capturas. Ainda não disponível a partir de 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 é encontrado primeiro.

Se o regexps do seu sistema suportar \s/ \S, você também poderá:

re='->\s*\S+'
[[ $var =~ $re ]]

Com 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

Portably:

$ expr " $var" : '.*\(->[[:space:]]*[^[:space:]]\{1,\}\)'
-> r1-ae0-2

Se houver várias ocorrências do padrão na cadeia, o comportamento variará com todas essas soluções. No entanto, nenhum deles fornecerá uma lista separada por nova linha de todas as correspondências, como no seu GNU-grep solução baseada .

Para fazer isso, você precisaria 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ê pode recorrer a esse tipo de truque para armazenar todas as correspondências em uma matriz:

set -o extendedglob
matches=() n=0
: ${var//(#m)->[[:space:]]#[^[:space:]]##/${matches[++n]::=$MATCH}}
printf '%s\n' $matches

1 referências posteriores designam mais comumente um padrão que faz referência ao que foi correspondido por um grupo anterior. Por exemplo, a \(.\)\1expressão regular básica corresponde a um único caractere seguido pelo mesmo caractere (corresponde a aa, não a ab). Essa \1é uma referência a isso\(.\) grupo de captura no mesmo padrão.

ksh93suporta referências posteriores em seus padrões (por exemplo ls -d -- @(?)\1, listará os nomes de arquivos que consistem em dois caracteres idênticos), e não outros shells. BREs e PCREs padrão suportam referências anteriores, mas não o ERE padrão, embora algumas implementações do ERE o suportem como uma extensão. bashé [[ foo =~ re ]]usos EREs.

[[ aa =~ (.)\1 ]]

não vai combinar, mas

re='(.)\1'; [[ aa =~ $re ]]

pode se os EREs do sistema o suportarem.

Stéphane Chazelas
fonte
9

Você deseja excluir tudo até o primeiro ␣->␣(sem incluir a "seta") e após o último ␣/(incluindo o espaço e a barra).

string="Blah: -> r1-ae0-2 / [123]"
string=${string/*->/->}
string=${string/ \/*}

$stringagora será -> r1-ae0-2.

As mesmas duas substituições se transformaria -> s7-Gi0-0-1:1-US / Fooem -> s7-Gi0-0-1:1-US.

Kusalananda
fonte
3

Responder a isso definitivamente é impossível sem conhecer o formato exato de cada mensagem. No entanto, como abordagem geral, você pode imprimir certos campos específicos usando cut:

$ cut -d ' ' -f 2 <<< '-> s7-Gi0-0-1:1-US / Foo'
s7-Gi0-0-1:1-US

Ou você pode imprimir todas as enésimas colunas 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
l0b0
fonte