4

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

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

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

4

5 回答 5

4

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

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

于 2012-07-19T18:10:44.473 回答
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]}较长的正则表达式会完全不同,因为它包含整个匹配项。

于 2012-07-19T20:27:31.030 回答
1

根据@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 ,但这很好用。

于 2012-07-19T21:27:00.107 回答
0

虽然这是一个老问题(没有公认的答案),但正则表达式模式是否可以简化为:

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

通过删除内括号来找到一组更小(更简洁)的单词DO-BATCHBATCH-DO

它适用于你 18:10 的时间回答。${BASH_REMATCH[0]} 和 ${BASH_REMATCH[1]} 导致找到 2 个单词。

于 2013-05-25T14:58:34.193 回答
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'
于 2021-01-12T19:04:18.913 回答