【问题标题】:Why "not the following character" regex for Android is [!x] and not [^x]?为什么 Android 的“不是以下字符”正则表达式是 [!x] 而不是 [^x]?
【发布时间】:2019-12-24 00:08:57
【问题描述】:

我需要编写一个 bash 脚本,它将在 Android 设备上执行。除此之外,我需要这个脚本来计算字符串中特定字符的出现次数,因为wc(字数统计)实用程序在 Android shell 中不可用,我这样做是这样的:

my_string="oneX two threeX"; x_amount="${my_string//[^X]}"; echo $x_amount; echo "${#x_amount}"

当我在桌面上运行上述命令时,它会返回(正如我所料):

XX
2

但如果我在我的 Android 设备上执行相同的命令(通过adb shell),结果令我惊讶的是:

one two three
13

我已经知道(只是猜测)如果我用! 代替^,那么命令就变成了:

my_string="oneX two threeX"; x_amount="${my_string//[!X]}"; echo $x_amount; echo "${#x_amount}";

然后,在 Android 上,它会产生我期望的结果:

XX
2

虽然相同的命令在桌面上失败并显示以下消息:

event not found: X]

即使我已经弄清楚如何“让它发挥作用”,我还是想了解以下几点:

  1. 除了Android shell,还有什么地方使用[!X]而不是[^X]

  2. 这种表示法有什么特殊名称吗?

  3. Android 不支持[^X] 是否有任何具体原因?

PS:我需要在其上运行脚本的设备具有相当旧版本的 Android (4.4),所以这个“问题”可能是特定于 Android 版本的,即使是这种情况,问题以上保留。

【问题讨论】:

  • 您正在使用${my_string//[!X]},这是一个不使用正则表达式的参数扩展,仅使用括号表达式和POSIX 字符类。它用于 *NIX shell,例如在巴什。 Android 中可能有语法变化。

标签: android regex bash adb regex-negation


【解决方案1】:

首先:模式匹配有很多不同的符号; shell 在这里使用的不是正则表达式,而是"glob" (or "wildcard") 模式——similar to an RE in some ways, very different in others(类似于“*”的含义)。这些基本模式类型也有变体,glob 语法的不同变体(尤其是 bash 的“扩展 glob”语法)和正则表达式语法的许多变体(“基本”RE、“扩展”RE、Perl 兼容的 RE、等等等等……)。

了解您使用的工具所采用的语法并适当地调整您的模式非常重要。

现在,对于否定括号表达式,POSIX standard from 2004 是这样说的:

基本正则表达式中括号表达式的描述 IEEE Std 1003.1-2001 的基本定义卷,第 9.3.5 节,RE 括号表达式也应适用于模式括号表达式, 除了感叹号字符 ('!') 将替换 抑扬符('^')在一个“不匹配列表”中的作用 正则表达式符号。以 an 开头的括号表达式 不带引号的抑扬符会产生未指定的结果。

2018 version 类似,但略有乱码;不确定那里发生了什么。)

所以,! 实际上是这里接受的标准。但是bashzsh 都使用! 来引入历史扩展,所以显然已经决定最好也接受^ 以避免与历史机制发生冲突。

bash 接受 "${my_string//[^X]}""${my_string//[!X]}",但 zsh 误认为后者试图引用包含 X] 的早期命令,从而给出您看到的错误。

【讨论】:

  • 感谢您解释zsh 中的错误。考虑到它支持[!...] 语法,这可能会让人很困惑。
【解决方案2】:

Android 的 shell 是 mksh,witch 使用与 Bash 不同的 RegEx 或模式方言。

见: File name patterns in mksh's man-page:

    File name patterns
...
     [!...]  Like [...], except it matches any octet not inside the brackets.

让我们测试一些 shell 与字符串替换和负字符类模式[!...] 语法的兼容性:

#!/usr/bin/env bash

shells=( ash bash dash ksh93 mksh tcsh zsh )
compat=()
not_compat=()
for shell in "${shells[@]}"; do
  if [ "$(
    "$shell" <<'EOF' 2>/dev/null
my_string="oneX two threeX"
x_amount="${my_string//[!X]}"; echo "$x_amount${#x_amount}"
EOF
  )" = "XX2" ]; then
    compat+=("$shell")
  else
    not_compat+=("$shell")
  fi
done
echo "Shells that understands the [!...] negative class syntax:"
printf '%s\n' "${compat[@]}"
echo
echo "Shells that don't understand string substitution:"
printf '%s\n' "${not_compat[@]}"

输出:

Shells that understands the [!...] negative class syntax:
bash
ksh93
mksh
zsh

Shells that don't understand string substitution:
ash
dash
tcsh

另请注意,sed 不理解 POSIX 否定字符组表示法 [!...],即使禁用其 Gnu 扩展也是如此:

sed --posix 's/[!X]//g' <<<'oneX two threeX'
one two three

但是

sed --posix 's/[^X]//g' <<<'oneX two threeX'
XX

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-05
    • 1970-01-01
    • 2017-01-18
    • 1970-01-01
    相关资源
    最近更新 更多