一直在寻找一些关于使用bash的regex的更高级的regex信息,但没有找到太多信息。
这是一个概念,用一个简单的字符串:
myString="DO-BATCH BATCH-DO"
if [[ $myString =~ ([[:alpha:]]*)-([[:alpha:]]*) ]]; then
echo ${BASH_REMATCH[1]} #first perens
echo ${BASH_REMATCH[2]} #second perens
echo ${BASH_REMATCH[0]} #full match
fi
outputs:
BATCH
DO
DO-BATCH
第一场比赛(BATCH-DO)打得很好,但我如何才能打第二场比赛(DO-BATCH)?我只是在这里留白,找不到关于bash-regex的太多信息。
好的,所以我做这件事的一种方法是把它放在for循环中:
myString="DO-BATCH BATCH-DO"
for aString in ${myString[@]}; do
if [[ ${aString} =~ ([[:alpha:]]*)-([[:alpha:]]*) ]]; then
echo ${BASH_REMATCH[1]} #first perens
echo ${BASH_REMATCH[2]} #second perens
echo ${BASH_REMATCH[0]} #full match
fi
done
which outputs:
DO
BATCH
DO-BATCH
BATCH
DO
BATCH-DO
这很有效,但如果可能的话,我有点希望从一个正则表达式中提取所有内容。
在您的答案中,myString
不是一个数组,但您使用数组引用来访问它。这在Bash中有效,因为数组的第0个元素只能由变量名引用,反之亦然。这意味着你可以使用:
for aString in $myString; do
在这种情况下得到相同的结果。
在你的问题中,你说输出包括"BATCH-DO"。我得到了"DO-BATCH",所以我认为这是一个拼写错误。
在不使用for
循环的情况下获得额外字符串的唯一方法是使用更长的regex。顺便说一下,我建议将Bash正则表达式放在变量中。它使某些类型更容易使用(例如,那些包含空白或特殊字符的类型
pattern='(([[:alpha:]]*)-([[:alpha:]]*)) +(([[:alpha:]]*)-([[:alpha:]]*))'
[[ $myString =~ $pattern ]]
declare -p BASH_REMATCH #dump the array
输出:
declare -ar BASH_REMATCH='([0]="DO-BATCH BATCH-DO" [1]="DO-BATCH" [2]="DO" [3]="BATCH" [4]="BATCH-DO" [5]="BATCH" [6]="DO")'
如果要捕获单独的子字符串以及连字符短语,则需要额外的一组括号。如果你不需要单独的单词,你可以去掉括号的内部集合。
请注意,如果只需要提取子字符串,则不需要使用if
。您只需要if
就可以根据匹配执行条件操作。
还要注意,${BASH_REMATCH[0]}
与较长的regex会有很大的不同,因为它包含了整个匹配。
根据@Dennis Williamson的帖子,我搞砸了,最终得到了以下结果:
myString="DO-BATCH BATCH-DO"
pattern='(([[:alpha:]]*)-([[:alpha:]]*)) +(([[:alpha:]]*)-([[:alpha:]]*))'
[[ $myString =~ $pattern ]] && { read -a myREMatch <<< ${BASH_REMATCH[@]}; }
echo "${myString} -> ${myString}"
echo "${#myREMatch[@]} -> ${#myREMatch[@]}"
for (( i = 0; i < ${#myREMatch[@]}; i++ )); do
echo "${myREMatch[$i]} -> ${myREMatch[$i]}"
done
这很好,除了myString必须有2个值。所以我发布这篇文章是因为它有点有趣,我很喜欢摆弄它。但为了让它更通用,并解决任何数量的配对组(即DO-BATCH),我将使用我原始答案的修改版本:
myString="DO-BATCH BATCH-DO"
myRE="([[:alpha:]]*)-([[:alpha:]]*)"
read -a myString <<< $myString
for aString in ${myString[@]}; do
echo "${aString} -> ${aString}"
if [[ ${aString} =~ ${myRE} ]]; then
echo "${BASH_REMATCH[@]} -> ${BASH_REMATCH[@]}"
echo "${#BASH_REMATCH[@]} -> ${#BASH_REMATCH[@]}"
for (( i = 0; i < ${#BASH_REMATCH[@]}; i++ )); do
echo "${BASH_REMATCH[$i]} -> ${BASH_REMATCH[$i]}"
done
fi
done
我本想来一场类似perlre的多次比赛,但这很好。
尽管这是一个已有一年历史的问题(没有公认的答案),regex模式是否可以简化为:
myRE="([[:alpha:]]*-[[:alpha:]]*)"
通过删除内括号来找到一组更小(更简洁)的单词DO-BATCH
和BATCH-DO
?
它在你18:10的时间回答中对我有效${BASH_REMATCH[0]}和${BASH_REMATCH[1]}导致找到这两个字。
如果你不知道提前会有多少匹配,你可以使用这个:
#!/bin/bash
function handle_value {
local one=$1
local two=$2
echo "i found ${one}-${two}"
}
function match_all {
local current=$1
local regex=$2
local handler=$3
while [[ ${current} =~ ${regex} ]]; do
"${handler}" "${BASH_REMATCH[@]:1}"
# trim off the portion already matched
current="${current#${BASH_REMATCH[0]}}"
done
}
match_all
"DO-BATCH BATCH-DO"
'([[:alpha:]]*)-([[:alpha:]]*)[[:space:]]*'
'handle_value'