为什么Android的“不是以下字符”正则表达式是[!x]而不是[^ x]?

Fil*_* W. 1 regex bash android adb regex-negation

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

my_string="oneX two threeX"; x_amount="${my_string//[^X]}"; echo $x_amount; echo "${#x_amount}"
Run Code Online (Sandbox Code Playgroud)

当我在桌面上运行上述命令时,它返回(与我期望的一样):

XX
2
Run Code Online (Sandbox Code Playgroud)

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

one two three
13
Run Code Online (Sandbox Code Playgroud)

我想通了(只是猜测),如果我取代!^,从而使命令变为:

my_string="oneX two threeX"; x_amount="${my_string//[!X]}"; echo $x_amount; echo "${#x_amount}";
Run Code Online (Sandbox Code Playgroud)

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

XX
2
Run Code Online (Sandbox Code Playgroud)

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

event not found: X]
Run Code Online (Sandbox Code Playgroud)

甚至我也想出了如何使其“起作用”的方式,我想了解以下几点:

  1. 除了Android Shell,还有其他地方[!X]使用符号代替[^X]

  2. 这样的符号有什么特别的名字吗?

  3. [^X]Android是否不支持任何特定原因?

PS:我需要在其上运行脚本的设备具有相当旧的Android版本(4.4),因此此“ 问题 ”可能是特定于Android版本的,即使是这种情况,上面的问题仍然存在。

Léa*_*ris 6

Android的外壳是mksh,巫婆使用的是与Bash不同的RegEx或模式方言。

请参见: 文件名模式mksh的手册页

    File name patterns
...
     [!...]  Like [...], except it matches any octet not inside the brackets.
Run Code Online (Sandbox Code Playgroud)

让我们使用字符串替换和否定字符类模式[!...]语法测试一些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[@]}"
Run Code Online (Sandbox Code Playgroud)

输出:

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

Shells that don't understand string substitution:
ash
dash
tcsh
Run Code Online (Sandbox Code Playgroud)

另请注意,即使禁用了其Gnu扩展名,它sed也无法理解POSIX否定字符组表示法[!...]

sed --posix 's/[!X]//g' <<<'oneX two threeX'
Run Code Online (Sandbox Code Playgroud)
one two three
Run Code Online (Sandbox Code Playgroud)

sed --posix 's/[^X]//g' <<<'oneX two threeX'
Run Code Online (Sandbox Code Playgroud)
XX
Run Code Online (Sandbox Code Playgroud)