我有一个名为的变量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
有反向引用(或更准确地说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 年 4 月 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
如果字符串中多次出现该模式,则所有这些解决方案的行为都会有所不同。然而,它们都不会像基于 GNU 的grep
解决方案那样为您提供所有匹配项的换行符分隔列表。
为此,您需要手动进行循环。例如,与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
基本正则表达式匹配单个字符,后跟该同一字符(它匹配 on aa
,而不是 on ab
)。这是以相同模式\1
对该捕获组的反向引用。\(.\)
ksh93
确实支持其模式中的反向引用(例如ls -d -- @(?)\1
将列出由两个相同字符组成的文件名),而不是其他 shell。标准 BRE 和 PCRE 支持反向引用,但不支持标准 ERE,尽管某些 ERE 实现支持将其作为扩展。bash
's[[ foo =~ re ]]
使用 ERE。
[[ aa =~ (.)\1 ]]
不会匹配,但是
re='(.)\1'; [[ aa =~ $re ]]
如果系统的 ERE 支持的话可以。
答案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