Bash正则表达式捕获组

时间:2017-09-25 02:45:23

标签: regex bash grep pcre

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

"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 regex复制此功能。我尝试过像

这样的事情
echo "$email" | grep -oP "\"\K(.+?)(?=\")|'\K(.+?)(?=')|(\S+)"

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

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

如果我删除前面/后面的逻辑,我至少得到3个字符串,但第一个和最后一个仍然用引号括起来。在那种方法中,我将输出管道传输到read,这样我就可以单独将每个字符串添加到数组中,但我可以打开其他选项。

编辑:

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

8 个答案:

答案 0 :(得分:3)

您可以使用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 $1 ? $1 : $2}' 
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

答案 1 :(得分:1)

你的第一个表达很好;只需注意引号(当\存在时使用单引号)。最后用{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>

答案 2 :(得分:1)

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>

答案 3 :(得分:0)

您可以使用sed来实现这一目标,

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

答案 4 :(得分:0)

使用gawk您可以设置多行RS

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

答案 5 :(得分:0)

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

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

输出:

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

答案 6 :(得分:0)

使用GNU awk和FPAT define 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>

答案 7 :(得分:0)

我能做到的是有效的,但并不像我想要的代码那样简洁:

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

这给了我一个捕获组的数组,并按任意顺序处理输入,用双引号或单引号括起来,如果它没有空格,则根本没有。它还提供了数组中没有包装引号的元素。感谢所有的建议。