Поддерживает ли bash обратные ссылки в расширении параметров?

15

У меня есть имя переменной , descrкоторая может содержать строку Blah: -> r1-ae0-2 / [123], -> s7-Gi0-0-1:1-US / Fooи т.д. Я хочу , чтобы получить -> r1-ae0-2, -> s7-Gi0-0-1:1-USчасть из строки. На данный момент я использую descr=$(grep -oP '\->\s*\S+' <<< "$descr"для этого. Есть лучший способ это сделать? Возможно ли это сделать с помощью расширения параметров?

Мартин
источник

Ответы:

20

ksh93и zshиметь обратную ссылку (или, точнее, 1 , ссылки на группы захвата в замене), поддержку внутри ${var/pattern/replacement}, а не 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странице ${KSH_MATCH[1]}руководства также упоминается, что будущие версии будут поддерживать его для первой группы захвата. По состоянию на 2017-04-25 пока не доступно).

Однако с помощью bashвы можете сделать:

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

Что лучше, так как он проверяет, что шаблон найден первым.

Если регулярное выражение вашей системы поддерживает \s/ \S, вы также можете сделать:

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

С помощью zshвы можете получить полную мощность PCRE с:

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

С zsh -o extendedglob, смотрите также:

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

Портабельно:

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

Если в строке есть несколько вхождений шаблона, поведение будет зависеть от всех этих решений. Однако ни один из них не предоставит вам разделенный строкой список всех совпадений, как в вашем решении на grepоснове GNU .

Чтобы сделать это, вам нужно сделать цикл вручную. Например, с bash:

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

С помощью zshвы можете прибегнуть к такой хитрости, чтобы сохранить все совпадения в массиве:

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

1 обратные ссылки чаще обозначают шаблон, который ссылается на то, что было сопоставлено более ранней группой. Например, \(.\)\1основное регулярное выражение соответствует одному символу, за которым следует тот же символ (оно соответствует aa, а не включено ab). Это \1обратная ссылка на эту \(.\)группу захвата в том же шаблоне.

ksh93поддерживает обратные ссылки в своих шаблонах (например, ls -d -- @(?)\1будет перечислять имена файлов, состоящие из двух идентичных символов), а не другие оболочки. Стандартные BRE и PCRE поддерживают обратные ссылки, но не стандартные ERE, хотя некоторые реализации ERE поддерживают его как расширение. bash«S [[ foo =~ re ]]использует Eres.

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

не будет соответствовать, но

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

может, если ERE системы поддерживают это.

Стефан Шазелас
источник
9

Вы хотите удалить все до первого ␣->␣(не считая «стрелки») и после последнего ␣/(включая пробел и косую черту).

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

$stringсейчас будет -> r1-ae0-2.

Те же две замены превратятся -> s7-Gi0-0-1:1-US / Fooв -> s7-Gi0-0-1:1-US.

Кусалананда
источник
3

Ответ на этот вопрос невозможен без знания точного формата каждого сообщения. Однако в качестве общего подхода вы можете напечатать определенные поля, используя cut:

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

Или вы можете напечатать каждый n-й столбец, используяawk :

$ 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
источник