给定 bash 环境变量设置:
$ declare -g bs=$'\\' bsbs=$'\\\\' q="'";
此正则表达式将正确匹配单引号(“'”)文本的序列,其中此类文本可能包含转义的单引号:
"[${bs}${q}]((([^${bsbs}]?[^${bs}${q}])|(${bsbs}${bs}${q}))+)[${bs}${q}]"
$ echo "[${bs}${q}]((([^${bsbs}]?[^${bs}${q}])|(${bsbs}${bs}${q}))+)[${bs}${q}]"
[\']((([^\\]?[^\'])|(\\\'))+)[\']
(“[\']”中的反引号并不是严格要求的,但为了清楚起见而包含在内,以防万一有人试图将此值编码在单引号字符串中)。
问题在于如何最好地将其推广到任何转义引用字符,以及如何处理多个转义序列的运行;仅当输入转义字符的长度为奇数 ((n&1)==1) 大小(字节数)时,最后一个转义为 ACTIVE,最后一个字符为 INACTIVE(字符串的一部分),否则(转义数是 EVEN ((n&1)==0),则字符串包含转义次数的一半 (n>>1) 并且最后一个字符是 ACTIVE (即未转义)。
另外,在 sed 和 grep / egrep 中,这有一些问题:
o 匹配的子组可以占用后续的“\1+”组号,增加它们的数量 - 如果任何后续组不匹配 -
- 理想情况下,我希望能够表达该正则表达式,而无需任何可能影响后续子组编号的子组。
o 它根本不处理转义数,并且无法
识别由偶数转义数进行的引用未被转义。
所以我的问题是:
如何最好地仅使用 glibc 支持的 POSIX RE 或 grep / sed RE 解决这些问题?
IE。允许在 RegExp 内识别任意长度的奇数(有效转义)或偶数(无效转义)长度的转义序列?
我真的认为 POSIX RE 可以受益于处理此类问题的特殊语法,例如:
[\\]{1,}\#&1\?$A\:$B
其中 '}#&1' 表示对前一个 [\]{...} 组匹配的元素数量进行测试 'x & 1',而 ?x:y 表示“如果最后一个测试为真,则替换 x,否则 y在RE”。
然后,人们实际上可以轻松地表达这一点并安全地处理 RegExp 解析字符串中的任意数量的转义。如果没有像这样的新 RE 语法,如何做到这一点?
单独使用 RegExp 表达式,即使不是不可能/不可行,也是非常困难的。
还是我错了?
现在是否有一种简单的方法可以对现代 POSIX RE 中前一组的游程长度进行算术?
示例1:
$ declare -g bs=$'\\' bsbs=$'\\\\' q="'";
$ echo "'a quot\\'d string' 42" | sed -r 's/'"[${bs}${q}]((([^${bsbs}]?[^${bs}${q}])|(${bsbs}${bs}${q}))+)[${bs}${q}]"'[[:space:]]([0-9]+)/\1\t:\t\2/'
'a quot'd string : g
示例2:
$ echo "'a quot\\'d string' 42" |
sed -r 's/'"[${q}]((([^${bsbs}]?[^${q}])|(${bsbs}${q}))+)[${q}]"'[[:space:]]([0-9]+)/\1\t:\t\2/'
a quot\'d string : g
请注意如何删除提到的 ${bs}-es @rowboat ,并且结果仍然相同,就像仅使用 $bs 而不是 $bsbs 一样:
$ echo "'a quot\\'d string' 42" | sed -r 's/'"[${q}]((([^${bs}]?[^${q}])|(${bs}${q}))+)[${q}]"'[[:space:]]([0-9]+)/\1\t:\t\2/'
a quot\'d string : g
结论 :
我正在开发 glibc 提供的“regex(7) - POSIX.2 正则表达式”库的非 POSIX 扩展、PCRE、PERL、cl-ppcre(SBCL 的 Common Lisp RE 库)和Emacs 的 RE 库:
o 为任何命名的 POSIX 字符类定义后缀为“-esc”或“esc”的含义,例如'[[:spaceesc:]]' 或 '[^[:space-esc:]]' 或 '[[:quote-esc:]]' ,这意味着:通常是字符类 'X 成员的字符', 不是字符类“${X}esc”的成员(“${X}-esc”的同义词)IFF 它前面有奇数个转义字符 ('\':ASCII "\x5c" )。
All character sequences that are subject to an :*esc: character
class test will have legal '\\' , '\xXX', '\0OOO', or '\Uxxxxxx' or
'\uXXXX' sequences replaced by :
ASCII:\x5c , ASCII:\xXX (where XX are hex digits),
ASCII:\OOO (where OOO are Octal digits) ,
24-bit unicode value with code point xxxxxx (x: hex digit) , and
16-bit unicode value with code point xxxx (x: hex digit) ,
respectively.
Also '[[:quote:]]' and '[[:quoteesc:]]' classes must be
supported that select characters (or non-escaped chars)
with the Unicode 'Quotation Mark' binary attribute, and
'[[:punct:]]' or '[[:punctesc:]]' would similarly apply
to all (non-escaped) chars which have the Punctuation attribute.
Perhaps a similar '*cesc' or '*escc' character class suffixes
could be provided that support also the C escapes:
'\n','\r','\t','\v','\b','\l'... etc.
If the /
答案1
如果重点是像 shell 语言解释器一样标记 shell 代码,那么正则表达式将无济于事。
zsh shell 使用z
参数扩展标志公开其标记器(或者Z
可以采用选项来处理注释或更改换行符的处理),您可以将其与Q
参数扩展结合起来进行引号删除。
例如:
tokens() printf ' - « %s »\n' ${(Z[Cn])1}
tokens_dequoted() printf ' - « %s »\n' "${(@Q)${(Z[Cn])1}}"
将在第一个参数中报告所有 shell 标记,删除注释;第二个也删除了一层引用:
$ tokens ' foo "a b"; "" "$(echo "x y")" <<'"'qwe '\''qwe' #qwe"
- « foo »
- « "a b" »
- « ; »
- « "" »
- « "$(echo "x y")" »
- « << »
- « 'qwe '\''qwe' »
$ tokens_dequoted ' foo "a b"; "" "$(echo "x y")" <<'"'qwe '\''qwe' #qwe"
- « foo »
- « a b »
- « ; »
- « »
- « $(echo "x y") »
- « << »
- « qwe 'qwe »
您可以看到,为了执行相同的操作,您需要实现一个完整的 shell 解析器。
如果缩小范围,您可以使用 regexp 达到某个目的:仅考虑'...'
、"..."
和\
引号类型(而不是$'...'
),并且仅考虑空格作为分隔符并忽略双引号内的扩展。在 bash 4.4+ 中,与 zsh 相反,无论如何都无法处理代码中的 NUL 字节,而使用 GNU grep
,您可以执行以下操作:
tokens() {
local tokens
readarray -td '' tokens < <(printf %s "$1" |
grep -Ezo '(\\.|[^[:space:]\\"'\'']|'\''[^'\'']*'\''|"(\\.|[^\\"])*")+'
)
printf ' - « %s »\n' "${tokens[@]}"
}
然后:
$ tokens ' foo "a b"\c\\\" c\ d '" 'qwe'\''qwe'\"'\"qwe"
- « foo »
- « "a b"\c\\\" »
- « c\ d »
- « 'qwe'\''qwe'"'"qwe »
为了从中删除一层引用,我会求助于perl
(或者zsh
可以像上面看到的那样开箱即用)。
答案2
更好的答案:使用 pcre / PERL RegExps :
$ cat a.pcre
/^[']((?|(?:[^\\]?[^'\t\n\r])|(?:[\\]['\t\n\r]))*)[']\t((?|(?:[^\\]?[^\t])|(?:[\\][^\t\n\r]))+)/
'A quot\'d\ tab containing string' 42
$ pcretest < a.pcre
PCRE version 8.45 2021-06-15
re> data> 0: 'A quot'd\x09tab containing string'\x0942
1: A quot'd\x09tab containing string
2: 42
data>