
У меня есть переменная с именем 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"
для этого. Есть ли лучший способ сделать это? Можно ли также сделать это с помощью расширения параметров?
решение1
ksh93
и zsh
иметь поддержку обратных ссылок (или, точнее , ссылок на группы захвата в замене) внутри ${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]}
для первой группы захвата. По состоянию на 25.04.2017 еще недоступно).
Однако с помощью 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 ]]
использует ERE.
[[ aa =~ (.)\1 ]]
не будет соответствовать, но
re='(.)\1'; [[ aa =~ $re ]]
может, если ЭРЭ системы это поддерживают.
решение2
Вам нужно удалить все до первого символа ␣->␣
(не включая «стрелку») и после последнего ␣/
(включая пробел и слеш).
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