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

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

Tengo una variable llamada descrque puede contener una cadena Blah: -> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Fooetc. Quiero obtener la -> r1-ae0-2parte -> s7-Gi0-0-1:1-USde 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?

Respuesta1

ksh93y zshtener soporte de referencia inversa (o más exactamente 1 , referencias a grupos de captura en el reemplazo) en el interior ${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

( mkshLa página de manual también menciona que las versiones futuras lo admitirán ${KSH_MATCH[1]}para el primer grupo de captura. Aún no está disponible a partir del 25 de abril de 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 primero comprueba que se encuentra el patrón.

Si las expresiones regulares de su sistema admiten \s/ \S, también puede hacer:

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

Con zsh, puedes obtener todo el poder de los PCRE con:

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

Con zsh -o extendedglob, ver también:

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

Portátilmente:

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

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

Para hacer eso, necesitarás 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, podrías 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 anteriores designan más comúnmente 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 anteriores en sus patrones (por ejemplo, ls -d -- @(?)\1enumerará los nombres de archivos que constan de dos caracteres idénticos), no otros shells. Los BRE y PCRE estándar admiten referencias anteriores, pero no el ERE estándar, aunque algunas implementaciones de ERE lo admiten como una extensión. bashUtiliza [[ foo =~ re ]]ERE.

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

no coincidirá, pero

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

podrá si los ERE del sistema lo soportan.

Respuesta2

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

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.

Respuesta3

Responder esto definitivamente es imposible sin conocer el formato exacto.cadamensaje toma. Sin embargo, como método general, puede imprimir ciertos campos específicos usando cut:

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

O tu puedesimprima 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

información relacionada