¿Bash admite referencias anteriores en la expansión de parámetros?

15

Tengo una variable llamada descrque puede contener una cadena Blah: -> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Fooetc. Quiero conseguir el -> r1-ae0-2, -> s7-Gi0-0-1:1-USparte de la cadena. Por el momento lo uso descr=$(grep -oP '\->\s*\S+' <<< "$descr"para esto. ¿Hay una mejor manera de hacer esto? ¿También es posible hacer esto con la expansión de parámetros?

Martín
fuente

Respuestas:

20

ksh93y zshtener respaldo de referencia (o más exactamente 1 , referencias a grupos de captura en el reemplazo), soporte interno ${var/pattern/replacement}, no 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

(La mkshpágina de manual también menciona que las versiones futuras lo admitirán ${KSH_MATCH[1]}para el primer grupo de captura. Todavía no está disponible a partir del 25/04/2017).

Sin embargo, con bash, puedes hacer:

$ [[ $var =~ -\>[[:space:]]*[^[:space:]]+ ]] &&
  printf '%s\n' "${BASH_REMATCH[0]}"
-> r1-ae0-2

Lo cual es mejor ya que comprueba que el patrón se encuentra primero.

Si la expresión regular de su sistema es compatible con \s/ \S, también puede hacer:

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

Con zsh, puede obtener toda la potencia de los PCRE con:

$ set -o rematchpcre
$ [[ $var =~ '->\s*\S+' ]] && printf '%s\n' $MATCH
-> r1-ae0-2

Con zsh -o extendedglob, vea también:

$ printf '%s\n' ${(SM)var##-\>[[:space:]]#[^[:space:]]##}
-> r1-ae0-2

Portablemente:

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

Si hay varias ocurrencias del patrón en la cadena, el comportamiento variará con todas esas soluciones. Sin embargo, ninguno de ellos le dará una lista separada por líneas nuevas de todas las coincidencias como en su grepsolución basada en GNU .

Para hacer eso, deberías hacer el bucle a mano. Por ejemplo, con bash:

re='(->\s*\S+)(.*)'
while [[ $var =~ $re ]]; do
  printf '%s\n' "${BASH_REMATCH[1]}"
  var=${BASH_REMATCH[2]}
done

Con zsh, puedes recurrir a este tipo de truco para almacenar todas las coincidencias en una matriz:

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

1 las referencias inversas generalmente designan un patrón que hace referencia a lo que coincidió con un grupo anterior. Por ejemplo, la \(.\)\1expresión regular básica coincide con un solo carácter seguido de ese mismo carácter (coincide con aa, no con ab). Esa \1es una referencia a ese \(.\)grupo de captura en el mismo patrón.

ksh93admite referencias posteriores en sus patrones (por ejemplo ls -d -- @(?)\1, enumerará los nombres de archivo que constan de dos caracteres idénticos), no otros shells. Los BRE y PCRE estándar admiten referencias posteriores pero no ERE estándar, aunque algunas implementaciones de ERE lo admiten como una extensión. bash's [[ foo =~ re ]]usa ERE.

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

no coincidirá, pero

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

mayo si los ERE del sistema lo admiten.

Stéphane Chazelas
fuente
9

Desea eliminar todo hasta el primero ␣->␣(sin incluir la "flecha") y después del último ␣/(incluido el espacio y la barra inclinada).

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

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

Las mismas dos sustituciones se convertirían -> s7-Gi0-0-1:1-US / Fooen -> s7-Gi0-0-1:1-US.

Kusalananda
fuente
3

Responder esto definitivamente es imposible sin saber el formato exacto que toma cada mensaje. Sin embargo, como enfoque general, puede imprimir ciertos campos específicos utilizando cut:

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

O puede imprimir cada enésima columna 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
fuente