Bash 正则表达式捕获组

Bash Regex Capture Groups

我有一个字符串是这种格式:

"Mike H<michael.haken@email1.com>" michael.haken@email2.com "Mike H<hakenmt@email1.com>"

如果我用 JS、C# 等编写普通的正则表达式,我会这样做

(?:"(.+?)"|'(.+?)'|(\S+))

并迭代匹配组以获取每个字符串,最好不带引号。我最终想将每个值添加到数组中,因此在示例中,我最终在数组中得到 3 个项目,如下所示:

Mike H<michael.haken@email1.com>
michael.haken@email2.com 
Mike H<hakenmt@email1.com>

我不知道如何使用 grepsed 或 bash 正则表达式复制此功能。我试过一些东西,比如

echo "$email" | grep -oP "\"\K(.+?)(?=\")|'\K(.+?)(?=')|(\S+)"

这个问题是虽然它有点模仿捕获组的功能,但它并不真正适用于倍数,所以我得到像

这样的捕获
"Mike
H<michael.haken@email1.com>"
 michael.haken@email2.com 

如果我删除 look ahead/behind 逻辑,我至少得到 3 个字符串,但第一个和最后一个仍然用引号引起来。在这种方法中,我将输出通过管道传输到 read,这样我就可以将每个字符串单独添加到数组中,但我对其他选项持开放态度。

编辑:

我认为我的输入示例可能令人困惑,这只是一个可能的输入。实际输入可以是双引号、单引号或非引号(无空格)的字符串,以任意顺序任意数量。 Javascript/C# 我提供的正则表达式是我想要实现的真实行为。

您可以使用 sed 来实现,

$ sed -r 's/"(.*)" (.*)"(.*)"/\n\n/g' <<< "$EMAIL"
Mike H<michael.haken@email1.com>
michael.haken@email2.com 
Mike H<hakenmt@email1.com>

您可以使用 Perl:

$ email='"Mike H<michael.haken@email1.com>" michael.haken@email2.com "Mike H<hakenmt@email1.com>"'
$ echo "$email" | perl -lane 'while (/"([^"]+)"|(\S+)/g) {print  ?  : }' 
Mike H<michael.haken@email1.com>
michael.haken@email2.com
Mike H<hakenmt@email1.com>

或者在纯 Bash 中,它有点罗嗦:

re='\"([^\"]+)\"[[:space:]]*|([^[:space:]]+)[[:space:]]*'
while [[ $email =~ $re ]]; do
    echo ${BASH_REMATCH[1]}${BASH_REMATCH[2]}
    i=${#BASH_REMATCH}
    email=${email:i}
done 
# same output

使用gawk可以设置多行RS

awk -v RS='"|" ' 'NF' inputfile
Mike H<michael.haken@email1.com>
michael.haken@email2.com
Mike H<hakenmt@email1.com>

像这样修改你的正则表达式:

grep -oP '("?\s*)\K.*?(?=")' file

输出:

Mike H<michael.haken@email1.com>
michael.haken@email2.com
Mike H<hakenmt@email1.com>

你的第一个表情没问题;请注意引号(当存在 \ 时使用单引号)。最后trim用sed."

$ echo $mail | grep -Po '".*?"|\S+' | sed -r 's/"$|^"//g'
Mike H<michael.haken@email1.com>
michael.haken@email2.com
Mike H<hakenmt@email1.com>

gawk + bash解决方案(将每一项添加到数组中):

email_str='"Mike H<michael.haken@email1.com>" michael.haken@email2.com "Mike H<hakenmt@email1.com>"'

readarray -t email_arr < <(awk -v FPAT="[^\"'[:space:]]+[^\"']+[^\"'[:space:]]+" \
                         '{ for(i=1;i<=NF;i++) print $i }' <<<$email_str)

现在,所有项目都在 email_arr

正在访问第二项:

echo "${email_arr[1]}"
michael.haken@email2.com

正在访问第 3 项:

echo "${email_arr[3]}"
Mike H<hakenmt@email1.com>

使用 GNU awk 和 FPATdefine fields by content:

$ awk '
BEGIN { FPAT="([^ ]*)|(\"[^\"]*\")" }  # define a field to be space-separated or in quotes
{
    for(i=1;i<=NF;i++) {               # iterate every field
        gsub(/^\"|\"$/,"",$i)          # remove leading and trailing quotes
        print $i                       # output
    }
}' file
Mike H<michael.haken@email1.com>
michael.haken@email2.com
Mike H<hakenmt@email1.com>

我能够做到的是可行的,但不如我希望的代码那样简洁:

arr=()
while read line; do
  line="${line//\"/}"
  arr+=("${line//\'/}")
done < <(echo $email | grep -oP "\"(.+?)\"|'(.+?)'|(\S+)")

这给了我一个捕获组的数组,并以任何顺序处理输入,用双引号或单引号引起来,如果没有 space,则完全用 none 引起来。它还提供了数组中没有引号的元素。感谢所有的建议。