最好/有什么方法可以计算 glibc regex(7) / GNU sed / grep / egrep 正则表达式中的转义数?

最好/有什么方法可以计算 glibc regex(7) / GNU sed / grep / egrep 正则表达式中的转义数?

给定 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> 

相关内容