在bash中使用正则表达式的字符串中的多个匹配项

时间:2012-07-19 16:43:55

标签: bash bash4

一直在寻找有关正则表达式的一些更高级的正则表达式信息,并且没有找到关于它的更多信息。

这是概念,带有一个简单的字符串:

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正则表达式的很多信息。

5 个答案:

答案 0 :(得分:3)

好的,所以我这样做的一种方法就是把它放在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

哪种方法有效,但我希望尽可能从一个正则表达式中提取所有内容。

答案 1 :(得分:1)

在您的回答中,myString不是数组,但您使用数组引用来访问它。这适用于Bash,因为数组的第0个元素只能通过变量名来引用,反之亦然。这意味着您可以使用:

for aString in $myString; do

在这种情况下获得相同的结果。

在你的问题中,你说输出包括“BATCH-DO”。我得到“DO-BATCH”所以我认为这是一个错字。

在不使用for循环的情况下获取额外字符串的唯一方法是使用更长的正则表达式。顺便说一句,我建议将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]}与较长的正则表达式完全不同,因为它包含整个匹配。

答案 2 :(得分:1)

Per @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,但这很好。

答案 3 :(得分:0)

虽然这是一年前的问题(没有接受答案),但是正则表达式模式可以简化为:

myRE="([[:alpha:]]*-[[:alpha:]]*)"

删除内括号以找到更小(更简洁)的单词DO-BATCHBATCH-DO

18:10时间答案对你有用。 $ {BASH_REMATCH [0]}和$ {BASH_REMATCH [1]}导致找到2个单词。

答案 4 :(得分:0)

如果您实际上不知道提前会有多少匹配,您可以使用这个:

#!/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'