在bash中使用regex在字符串中进行多个匹配

  • 本文关键字:字符串 bash regex bash bash4
  • 更新时间 :
  • 英文 :


一直在寻找一些关于使用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-BATCHBATCH-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'

相关内容

  • 没有找到相关文章