在正则表达式模式匹配中转义双引号的正确语法?

Correct Syntax For Escaping Double Quotes in Regex Pattern Match?

我正在尝试获取 vars string 和 string2 中双引号字符之间的第二个子字符串。

我认为问题在于我试图转义双引号的方式。

正确的语法是什么:

#!/bin/bash

# Example strings.

string='"name": "Bash scripting cheatsheet",'
string2='"url": "https://devhints.io/bash"'

# I'm trying to get the 2nd substring between " "

# desired matches:
# string_name_match='Bash scripting cheatsheet'
# string2_url_match='https://devhints.io/bash'

# Attempts: using a pattern var with double quotes escaped.

pattern='\".*\"'  # Is the " char escaped correctly?
echo "$string" | awk "/$pattern/{print }" # Is the $pattern var used correctly?
echo "$string2" | awk "/$pattern/{print }" 

# 2nd pattern match using the name/url to parse:

name_pattern='^\"name:\"[:space:].*[^\",]'
url_pattern='^\"url\"[:space:]\"^url:.*[^"]'
echo "$string" | awk "/$name_pattern/{print [=10=]}"
echo "$string2" | awk "/$url_pattern/{print [=10=]}"

以下是您在 awk 中的操作方式:

awk -F '"' -v n=2 '{print $(n*2)}' <<< "$string"
Bash scripting cheatsheet

awk -F '"' -v n=2 '{print $(n*2)}' <<< "$string2"
https://devhints.io/bash

解决当前将正则表达式传递给 awk 的问题,由于转义序列的各种问题,通常更容易处理变量而不是 hard-coded 正则表达式模式,并结合测试整行([=15=]) 反对模式 (~ pattern_variable),例如:

string='"name": "Bash scripting cheatsheet",'
string2='"url": "https://devhints.io/bash"'
pattern='"([^"]*)".*"([^"]*)"'

$ awk -v ptn="${pattern}" -F'"' '[=10=] ~ ptn {print }' <<< "${string}"
"Bash

$ awk -v ptn="${pattern}" '[=10=] ~ ptn {print }' <<< "${string2}"
"https://devhints.io/bash"

好的,所以我们 awk 使用正则表达式,但我们没有得到我们想要的,因为默认情况下 awk 使用白色 space 作为默认字段分隔符。我们可以告诉 awk 使用双引号作为分隔符,并且知道我们想要的值在第二组双引号之间:

$ awk -v ptn="${pattern}" -F'"' '[=11=] ~ ptn {print }' <<< "${string}"
Bash scripting cheatsheet

$ awk -v ptn="${pattern}" -F'"' '[=11=] ~ ptn {print }' <<< "${string2}"
https://devhints.io/bash

'当然,每次我们要解析字符串时都需要生成一个子进程。

有一些(更好的)方法可以在 bash 中解析字符串,而无需产生子进程调用的开销 ...


使用一些基本 bash 正则表达式匹配的想法:

string='"name": "Bash scripting cheatsheet",'
string2='"url": "https://devhints.io/bash"'
pattern='"([^"]*)".*"([^"]*)"'

如果 bash 找到匹配项,它将使用有关匹配项的信息填充 BASH_REMATCH[] 数组,每个捕获组(一组括号内的模式部分)使在数组中增加一个单独的条目。

考虑:

$ [[ "${string}" =~ ${pattern} ]] && string_name_match="${BASH_REMATCH[2]}"
$ typeset -p BASH_REMATCH string_name_match
declare -ar BASH_REMATCH=([0]="\"name\": \"Bash scripting cheatsheet\"" [1]="name" [2]="Bash scripting cheatsheet")
declare -- string_name_match="Bash scripting cheatsheet"

$ echo "${string_name_match}"
Bash scripting cheatsheet



$ [[ "${string2}" =~ ${pattern} ]] && string2_url_match="${BASH_REMATCH[2]}"
$ typeset -p BASH_REMATCH string2_url_match
declare -ar BASH_REMATCH=([0]="\"url\": \"https://devhints.io/bash\"" [1]="url" [2]="https://devhints.io/bash")
declare -- string2_url_match="https://devhints.io/bash"

$ echo "${string2_url_match}"
https://devhints.io/bash

使用您显示的示例,请尝试以下 grep 代码。在 GNU grep.

中编写和测试
echo "$string" | grep -oP '.*?"[^"]*".*?"\K[^"]*'
Bash scripting cheatsheet

echo "$string2" | grep -oP '.*?"[^"]*".*?"\K[^"]*'
https://devhints.io/bash

说明: 这里使用 GNU grep。通过 echo 命令打印字符串值并将其作为标准输入发送到 grep 命令。在 grep 命令中使用正则表达式 .*?"[^"]*".*?"\K[^"]*(在下面解释)来实现所需的输出。

正则表达式的解释(.*?"[^"]*".*?"\K[^"]*):

.*?"    ##using lazy match capability of GNU grep and matching till very first occurrence of " here.
[^"]*"  ##Then matching everything just before next occurrence of " including " here.
.*?"    ##Using lazy match to match till very next occurrence of " here, which will be 3rd occurrence of ".
\K      ##Now using magical \K option of GNU grep to forget(basically not to print) whatever was matched before.
[^"]*   ##Matching everything just before 4th occurrence of " which is required output.

这是另一个简单的解决方案:

使用 gawk 标准 Linux awkFPAT 变量是匹配数据字段的正则表达式。

echo '"url": "https://devhints.io/bash"' |awk -vFPAT='[^\"]*' '{print }'
https://devhints.io/bash

您可以使用 Bash 正则表达式:

$ [[ $string =~ ^([^\"]*\"){4} ]] && echo "${BASH_REMATCH[1]%\"}"
Bash scripting cheatsheet

$ [[ $string2 =~ ^([^\"]*\"){4} ]] && echo "${BASH_REMATCH[1]%\"}"
https://devhints.io/bash

或与sed相同的方法:

sed -E 's/^([^"]*\"){4}//; s/".*//' <<<"$string"
Bash scripting cheatsheet

sed -E 's/^([^"]*\"){4}//; s/".*//' <<<"$string2"
https://devhints.io/bash

(但是 sed 不需要转义 "...)